/*___INFO__MARK_BEGIN__*/
/*************************************************************************
 * 
 *  The Contents of this file are made available subject to the terms of
 *  the Sun Industry Standards Source License Version 1.2
 * 
 *  Sun Microsystems Inc., March, 2001
 * 
 * 
 *  Sun Industry Standards Source License Version 1.2
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.2 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://gridengine.sunsource.net/Gridengine_SISSL_license.html
 * 
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 * 
 *   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 * 
 *   Copyright: 2001 by Sun Microsystems, Inc.
 * 
 *   All Rights Reserved.
 * 
 ************************************************************************/
/*___INFO__MARK_END__*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

/* do not compile in monitoring code */
#ifndef NO_SGE_COMPILE_DEBUG
#define NO_SGE_COMPILE_DEBUG
#endif

#include "sge_log.h"
#include "sgermon.h"
#include "cull_listP.h"
#include "cull_list.h"
#include "cull_db.h"
#include "cull_parse.h"
#include "cull_multitype.h"
#include "cull_whatP.h"
#include "cull_lerrnoP.h"

#include "pack.h"
#include "cull_pack.h"
#include "msg_gdilib.h"

static lEnumeration *subscope_lWhat(cull_parse_state* state, va_list *app);

/****** cull/what/nm_set() ****************************************************
*  NAME
*     nm_set() -- Build a int vector 
*
*  SYNOPSIS
*     void nm_set(int job_field[], int nm) 
*
*  FUNCTION
*     Build a int vector like it is used by lIntVector2What() 
*
*  INPUTS
*     int job_field[] - int vector 
*     int nm          - field name id  
******************************************************************************/
void nm_set(int job_field[], int nm) 
{
   int i;

   DENTER(TOP_LAYER, "nm_set");

   /* seek it */
   for (i=0; job_field[i]!=NoName; i++)
      if (job_field[i] == nm) {
         DRETURN_VOID; /*found*/
      }

   /* set it */
   DPRINTF(("%s\n", lNm2Str(nm)));
   job_field[i++] = nm;
   job_field[i] = NoName;

   DRETURN_VOID;
}

/****** cull/what/lReduceDescr() **********************************************
*  NAME
*     lReduceDescr() -- Reduce a descriptor 
*
*  SYNOPSIS
*     int lReduceDescr(lDescr **dst_dpp, lDescr *src_dp, lEnumeration *enp) 
*
*  FUNCTION
*     Makes a new descriptor in 'dst_dpp' that containes only those 
*     fields from 'src_dp' that are in 'enp'. 
*
*  INPUTS
*     lDescr **dst_dpp  - destination for reduced descriptor 
*     lDescr *src_dp    - source descriptor 
*     lEnumeration *enp - condition 
*
*  RESULT
*     int - error state
*         0 - OK
*        -1 - Error
*******************************************************************************/
int lReduceDescr(lDescr **dst_dpp, lDescr *src_dp, lEnumeration *enp) 
{
   int n, index = 0;

   DENTER(TOP_LAYER, "lReduceDescr");

   if (!dst_dpp || !src_dp || !enp) {
      DRETURN(-1);
   }

   n = lCountWhat(enp, src_dp);

   if (n == 0) {
      DRETURN(0);
   }

   /* create new partial descriptor */
   if (!(*dst_dpp = (lDescr *) malloc(sizeof(lDescr) * (n + 1)))) {
      DRETURN(-1);
   }
   lPartialDescr(enp, src_dp, *dst_dpp, &index);

   DRETURN(0);
}

/* ------------------------------------------------------------ 
   _lWhat creates an enumeration array. This is used in lWhat
   to choose special fields of a list element.
 */
lEnumeration *_lWhat(const char *fmt, const lDescr *dp, 
                     const int *nm_list, int nr_nm) 
{
   int neg = 0;
   int i, j, k, n, size = 0;
   const char *s;
   lEnumeration *ep = NULL;
   lEnumeration *ep2 = NULL;
   int error_status;
   cull_parse_state state;

   DENTER(CULL_LAYER, "_lWhat");

   if (!fmt) {
      error_status = LENOFORMATSTR;
      goto error;
   }
   /* how many fields are selected (for malloc) */
   for (n = 0, s = fmt; *s; s++)
      if (*s == '%')
         n++;

   /* are the number of %'s the number of names - 1 ? */
   if (nr_nm != n - 1) {
      error_status = LESYNTAX;
      goto error;
   }

   memset(&state, 0, sizeof(state));
   if (scan(fmt, &state) != TYPE) {
      /* %T expected */
      error_status = LESYNTAX;
      goto error;
   }
   eat_token(&state);                 /* eat %T */
   if (scan(NULL, &state) != BRA) {
      /* ( expected */
      error_status = LESYNTAX;
      goto error;
   }
   eat_token(&state);                 /* eat ( */

   /* Nr of fields to malloc */
   /* for CULL_ALL and NONE we need one lEnumeration struct */
   if (n > 1)
      n--;
   if (!(ep = (lEnumeration *) malloc(sizeof(lEnumeration) * (n + 1)))) {
      error_status = LEMALLOC;
      goto error;
   }

   /* Special cases CULL_ALL fields are selected or NONE is selected */
   /* Normally the arguments nm_list and nr_nm should be NULL(0)     */
   switch (scan(NULL, &state)) {

   case CULL_ALL:
      ep[0].pos = WHAT_ALL;
      ep[0].nm = -99;
      ep[0].mt = -99;
      ep[0].ep = NULL;
      eat_token(&state);
      ep[1].pos = 0;
      ep[1].nm = NoName;
      ep[1].mt = lEndT;
      ep[1].ep = NULL;
      DRETURN(ep);

   case CULL_NONE:
      ep[0].pos = WHAT_NONE;
      ep[0].nm = -99;
      ep[0].mt = -99;
      ep[0].ep = NULL;
      eat_token(&state);
      ep[1].pos = 0;
      ep[1].nm = NoName;
      ep[1].mt = lEndT;
      ep[1].ep = NULL;
      DRETURN(ep);

   default:
      break;
   }

   if (scan(NULL, &state) == NEG) {
      neg = 1;
      if ((size = lCountDescr(dp)) == -1) {
         error_status = LECOUNTDESCR;
         goto error;
      }
      /* subtract the fields that shall not be enumerated */
      size -= n;
      eat_token(&state);              /* eat ! */
      if (scan(NULL, &state) != BRA) {
         /* ( expected */
         error_status = LESYNTAX;
         goto error;
      }
      else
         eat_token(&state);           /* eat_token BRA, if it was a BRA */
   }

   for (i = 0; i < n; i++) {

      ep[i].nm = nm_list[i];
      if ((ep[i].pos = lGetPosInDescr(dp, ep[i].nm)) < 0) {
         error_status = LENAMENOT;
         goto error;
      }
      ep[i].mt = dp[ep[i].pos].mt; 
      ep[i].ep = NULL;

      if (scan(NULL, &state) != FIELD) {
         error_status = LESYNTAX;
         goto error;
      }
      eat_token(&state);

   }

   ep[n].pos = 0;
   ep[n].nm = NoName;
   ep[n].mt = lEndT;
   ep[n].ep = NULL;

   if (neg) {
      if (scan(NULL, &state) != KET) {
         error_status = LESYNTAX;
         goto error;
      }
      eat_token(&state);

      if (!(ep2 = (lEnumeration *) malloc(sizeof(lEnumeration) * (size + 1)))) {
         error_status = LEMALLOC;
         goto error;
      }

      k = 0;
      for (i = 0; dp[i].nm != NoName; i++) {
         /* remove the elements listed in the format string */
         for (j = 0; ep[j].nm != NoName && ep[j].nm != dp[i].nm; j++);
         if (ep[j].nm != NoName)
            continue;
         ep2[k].pos = i;
         ep2[k].nm = dp[i].nm;
         ep2[k].mt = dp[i].mt;
         ep2[k].ep = NULL;
         k++;
      }
      ep2[k].pos = 0;
      ep2[size].nm = NoName;
      ep2[size].mt = lEndT;
      ep2[size].ep = NULL;
      lFreeWhat(&ep);
      ep = ep2;
   }

   if (scan(NULL, &state) != KET) {
      error_status = LESYNTAX;
      goto error;
   }
   eat_token(&state);                 /* eat ) */

   DRETURN(ep);

 error:
   LERROR(error_status);
   lFreeWhat(&ep);
   DPRINTF(("error_status = %d\n", error_status));
   DRETURN(NULL);
}

/****** cull/what/lWhat() *****************************************************
*  NAME
*     lWhat() -- Create a ne enumeration 
*
*  SYNOPSIS
*     lEnumeration *lWhat(const char *fmt, ...) 
*
*  FUNCTION
*     Create a new enumeration. fmt describes the format of the enumeration 
*
*  INPUTS
*     const char *fmt - format string: 
*
*                          element := type "(" attribute_list ")" .
*                          type := "%T" .
*                          attribute_list := "ALL" | "NONE" | attributes .
*                          attributes = { "%I" | "%I" "->" element } .
*
*                       examples:
*                          1) "%T(NONE)"
*                          2) "%T(ALL)"
*                          3) "%T(%I%I)"
*                          4) "%T(%I%I->%T(%I%I))"
*
*     ...             - varibale list of arguments
*              
*                       varargs corresponding to examples above:
*                          1) JB_Type
*                          2) JB_Type
*                          3) JB_Type JB_job_numer JB_ja_tasks
*                          4) JB_Type JB_job_numer JB_ja_tasks
*                                JAT_Type JAT_task_number JAT_status
*                       
*
*  RESULT
*     lEnumeration* - new enumeration
*
*  NOTES
*     "%I" is equivalent with "%I->%T(ALL)" 
*     "" is NOT equivalent with "%I->%T(NONE)"
*******************************************************************************/
lEnumeration *lWhat(const char *fmt, ...)
{
   lEnumeration *enumeration = NULL;
   va_list ap;
   cull_parse_state state;

   DENTER(CULL_LAYER, "lWhat");
   va_start(ap, fmt);

   if (!fmt) {
      LERROR(LENOFORMATSTR);
      DRETURN(NULL);
   }
   /* 
      initialize scan function, the actual token is scanned again 
      in the subscope fuction. There we call eat_token to go ahead
    */
   memset(&state, 0, sizeof(state));
   scan(fmt, &state);
   
   /* parse */
   enumeration = subscope_lWhat(&state, &ap);

   if (!enumeration) {
      LERROR(LEPARSECOND);
      DRETURN(NULL);
   }

   DRETURN(enumeration);
}

static lEnumeration *subscope_lWhat(cull_parse_state* state, va_list *app)
{
   lDescr *dp = NULL;
   lEnumeration *enumeration = NULL;
   int token;
   lEnumeration ep[1000];
   int next_id = 0;
   int i;

   DENTER(CULL_LAYER, "subscope_lWhat");

   if (scan(NULL, state) != TYPE) {
      LERROR(LESYNTAX);
      DRETURN(NULL);
   }
   eat_token(state);                 /* eat %T */
   if (!(dp = va_arg(*app, lDescr *))) {
      LERROR(LEDESCRNULL);
      DRETURN(NULL);
   }

   if (scan(NULL, state) != BRA) {
      LERROR(LESYNTAX);
      DRETURN(NULL);
   }
   eat_token(state);                 /* eat ( */

   token = scan(NULL, state);

   if (token == CULL_ALL) {
      ep[next_id].pos = WHAT_ALL;
      ep[next_id].nm = -99;
      ep[next_id].mt = -99;
      ep[next_id].ep = NULL;
      next_id++;
      eat_token(state);
   } else if (token == CULL_NONE) {
      ep[next_id].pos = WHAT_NONE;
      ep[next_id].nm = -99;
      ep[next_id].mt = -99;
      ep[next_id].ep = NULL;
      next_id++;
      eat_token(state);
   } else {
      do {
         int nm;

         if (token != FIELD) {
            LERROR(LESYNTAX);         
            DRETURN(NULL);
         }
         eat_token(state);                 /* eat %I */

         /* find field in current descriptor */ 
         nm = va_arg(*app, int);     
         for (i = 0; dp[i].nm != NoName; i++) { 
            if (dp[i].nm == nm) {
               ep[next_id].pos = i;
               ep[next_id].nm = nm;
               ep[next_id].mt = dp[i].mt;
               break;
            } 
         }

         token = scan(NULL, state);
         if (token == SUBSCOPE) {
            eat_token(state);                 /* eat -> */
            ep[next_id].ep = subscope_lWhat(state, app);
            token = scan(NULL, state);
         } else {
            ep[next_id].ep = NULL;
         }
         next_id++;

      } while (token == FIELD);
   }

   /* add last element */
   ep[next_id].pos = 0;
   ep[next_id].nm = NoName;
   ep[next_id].mt = lEndT;
   ep[next_id].ep = NULL;
   next_id++;

   if (scan(NULL, state) != KET) {
      LERROR(LESYNTAX);
      DRETURN(NULL);
   }
   eat_token(state);                 /* eat ) */

   /* make copy of local data */
   enumeration = malloc(sizeof(lEnumeration) * next_id);
   if (enumeration == NULL) {
      LERROR(LEMALLOC);
      DRETURN(NULL);
   }

   for (i = 0; i < next_id; i++) {  
      enumeration[i].pos = ep[i].pos;
      enumeration[i].nm = ep[i].nm;
      enumeration[i].mt = ep[i].mt;
      enumeration[i].ep = ep[i].ep;
   }

   DRETURN(enumeration);
}

/****** cull/what/lWhatAll() *****************************************************
*  NAME
*     lWhatAll() -- Creates a enumeration array requesting all elements. 
*
*  SYNOPSIS
*     lEnumeration* lWhatAll() 
*
*  FUNCTION
*     Creates a enumeration array that requests complete elements 
*     of whatever typed list. This is a shortcut for 
*     lWhat("%T(ALL)", <List_type>)), cause for all the descriptor is not
*     needed anyway, it is available from the list itself.
*
*  INPUTS
*
*  RESULT
*     lEnumeration* - enumeration 
******************************************************************************/
lEnumeration *lWhatAll()
{
   lEnumeration *ep;
   int error_status;

   DENTER(CULL_LAYER, "lWhatAll");

   if (!(ep = (lEnumeration *) malloc(sizeof(lEnumeration) * 2))) {
      error_status = LEMALLOC;
      goto error;
   }

   ep[0].pos = WHAT_ALL;
   ep[0].nm = -99;
   ep[0].mt = -99;
   ep[0].ep = NULL;
   ep[1].pos = 0;
   ep[1].nm = NoName;
   ep[1].mt = lEndT;
   ep[1].ep = NULL;

   DRETURN(ep);

 error:
   LERROR(error_status);
   DPRINTF(("error_status = %d\n", error_status));
   DRETURN(NULL);
}

/****** cull/what/lFreeWhat() *************************************************
*  NAME
*     lFreeWhat() -- Frees a enumeration array 
*
*  SYNOPSIS
*     void lFreeWhat(lEnumeration **ep) 
*
*  FUNCTION
*     Frees a enumeration array 
*
*  INPUTS
*     lEnumeration **ep - enumeration, will be set to NULL 
*
******************************************************************************/
void lFreeWhat(lEnumeration **ep) 
{
   int i;

   DENTER(CULL_LAYER, "lFreeWhat");

   if (ep == NULL || *ep == NULL) {
      DRETURN_VOID;
   }
   for (i = 0; mt_get_type((*ep)[i].mt) != lEndT; i++) {
      if ((*ep)[i].ep != NULL) {
         lFreeWhat(&((*ep)[i].ep));
      }
   }
   FREE(*ep);
   DRETURN_VOID;
}

/****** cull/what/lCountWhat() ************************************************
*  NAME
*     lCountWhat() -- Returns size of enumeration 
*
*  SYNOPSIS
*     int lCountWhat(const lEnumeration *enp, const lDescr *dp) 
*
*  FUNCTION
*     Returns size of enumeration 
*
*  INPUTS
*     const lEnumeration *enp - enumeration 
*     const lDescr *dp        - descriptor 
*
*  RESULT
*     int - number of fields in enumeration 
******************************************************************************/
int lCountWhat(const lEnumeration *enp, const lDescr *dp) 
{
   int n;

   DENTER(CULL_LAYER, "lCountWhat");

   if (!enp) {
      LERROR(LEENUMNULL);
      DRETURN(-1);
   }
   if (!dp) {
      LERROR(LEDESCRNULL);
      DRETURN(-1);
   }
   switch (enp[0].pos) {
   case WHAT_NONE:
      n = 0;
      break;
   case WHAT_ALL:
      if ((n = lCountDescr(dp)) == -1) {
         LERROR(LECOUNTDESCR);
         DRETURN(-1);
      }
      break;
   default:
      for (n = 0; enp[n].nm != NoName; n++)
         ;
   }

   DRETURN(n);
}

/****** cull/what/lCopyWhat() *************************************************
*  NAME
*     lCopyWhat() -- Copy a enumeration array 
*
*  SYNOPSIS
*     lEnumeration* lCopyWhat(const lEnumeration *ep) 
*
*  FUNCTION
*     Copy a enumeration array 
*
*  INPUTS
*     const lEnumeration *ep - enumeration 
*
*  RESULT
*     lEnumeration* - new copy of enumeration 
******************************************************************************/
lEnumeration *lCopyWhat(const lEnumeration *ep) 
{
   int i, n;
   lEnumeration *copy = NULL;

   DENTER(CULL_LAYER, "lCopyWhat");

   if (!ep) {
      LERROR(LEENUMNULL);
      DRETURN(NULL);
   }

   for (n = 0; mt_get_type(ep[n].mt) != lEndT; n++);

   if (!(copy = (lEnumeration *) malloc(sizeof(lEnumeration) * (n + 1)))) {
      LERROR(LEMALLOC);
      DRETURN(NULL);
   }

   for (i = 0; i <= n; i++) {
      copy[i].pos = ep[i].pos;
      copy[i].nm = ep[i].nm;
      copy[i].mt = ep[i].mt;
      copy[i].ep = lCopyWhat(ep[i].ep);
   }

   DRETURN(copy);
}

/****** cull/what/lIntVector2What() *******************************************
*  NAME
*     lIntVector2What() -- Create a enumeration from int array 
*
*  SYNOPSIS
*     lEnumeration* lIntVector2What(const lDescr *dp, const int intv[]) 
*
*  FUNCTION
*     Create a enumeration from int array 
*
*  INPUTS
*     const lDescr *dp - descriptor 
*     const int intv[] - int array 
*
*  RESULT
*     lEnumeration* - enumeration 
******************************************************************************/
lEnumeration *lIntVector2What(const lDescr *dp, const int intv[]) 
{
   lEnumeration *what;
   char fmtstr[2000];
   int i;

   DENTER(CULL_LAYER, "lIntVector2What");

   /*
      reduce a descriptor to get one with only
      these fields that are needed for modify
    */
   strcpy(fmtstr, "%T(");
   for (i = 0; intv[i] != NoName; i++) {
      strcat(fmtstr, "%I");
   }
   strcat(fmtstr, ")");

   DPRINTF(("fmtstr: %s\n", fmtstr));

   what = _lWhat(fmtstr, dp, intv, i);

   DRETURN(what);
}

int lMergeWhat(lEnumeration **what1, lEnumeration **what2)
{
   int ret = 0;

   DENTER(CULL_LAYER, "lMergeWhat");
   if (*what1 == NULL || 
       (*what1)[0].pos == WHAT_NONE ||
       (*what2)[0].pos == WHAT_ALL) {
      /*
       * what1 is empty or what2 containes all attributes
       */
      lFreeWhat(what1);
      *what1 = *what2;
      *what2 = NULL;
   } else if ((*what1)[0].pos == WHAT_ALL) {
      /*
       * what1 contailes already all elements
       */
      lFreeWhat(what2);
   } else {
      lEnumeration tmp_result[1000];
      int next_id = 0;
      int i, j;

      /*
       * Add all elements of what1 
       */
      for (i = 0; mt_get_type((*what1)[i].mt) != lEndT; i++) { 
         tmp_result[next_id].pos = (*what1)[i].pos;
         tmp_result[next_id].mt = (*what1)[i].mt;
         tmp_result[next_id].nm = (*what1)[i].nm;
         tmp_result[next_id].ep = (*what1)[i].ep;
         (*what1)[i].ep = NULL;
         next_id++;
      }
      tmp_result[next_id].pos = 0;
      tmp_result[next_id].nm = NoName;
      tmp_result[next_id].mt = lEndT;
      tmp_result[next_id].ep = NULL;

      lFreeWhat(what1);

      /*
       * Add only those of what2 which are not already contained in what1
       * honour subwhat's
       */
      for (i = 0; mt_get_type((*what2)[i].mt) != lEndT; i++) {
         bool skip = false;

         for (j = 0; mt_get_type(tmp_result[j].mt) != lEndT; j++) {
            if (tmp_result[j].mt == (*what2)[i].mt &&
                tmp_result[j].nm == (*what2)[i].nm &&
                tmp_result[j].pos == (*what2)[i].pos) {
               /*TODO: SG is this correct? what happens, if we have a new subfilter
                 in what2 but none in temp? */
               if (tmp_result[next_id].ep != NULL && (*what2)[i].ep != NULL) {
                  lMergeWhat(&(tmp_result[next_id].ep), &((*what2)[i].ep));
               } else {
                  tmp_result[next_id].ep = NULL;
               }

               skip = true;
               break;
            }
         }
         if (!skip) {
            tmp_result[next_id].pos = (*what2)[i].pos;
            tmp_result[next_id].mt = (*what2)[i].mt;
            tmp_result[next_id].nm = (*what2)[i].nm;
            
            /*TODO: SG is this correct? what happens, if we have a new subfilter
              in what2 but none in temp? */
            if (tmp_result[next_id].ep != NULL && (*what2)[i].ep != NULL) {
               lMergeWhat(&(tmp_result[next_id].ep), &((*what2)[i].ep));
            } else { 
               tmp_result[next_id].ep = NULL;
            }
            /* add last element */
            next_id++;
            tmp_result[next_id].pos = 0;
            tmp_result[next_id].nm = NoName;
            tmp_result[next_id].mt = lEndT;
            tmp_result[next_id].ep = NULL;
         }
      }
      tmp_result[next_id].pos = 0;
      tmp_result[next_id].nm = NoName;
      tmp_result[next_id].mt = lEndT;
      tmp_result[next_id].ep = NULL;
      next_id++;
      lFreeWhat(what2);

      /*
       * create result what 
       */
      *what1 = malloc(sizeof(lEnumeration) * next_id);
      if (*what1 != NULL) {
         for (i = 0; i < next_id; i++) {
            (*what1)[i].pos = tmp_result[i].pos;
            (*what1)[i].nm = tmp_result[i].nm;
            (*what1)[i].mt = tmp_result[i].mt;
            (*what1)[i].ep = tmp_result[i].ep;
         }
      } else {
         LERROR(LEMALLOC);
         DTRACE;
         ret = -1;
      }
   }
   DRETURN(ret);
}

int lWhatSetSubWhat(lEnumeration *what1, int nm, lEnumeration **what2)
{
   int ret = -1;
   int i;

   DENTER(CULL_LAYER, "lWhatSetSubWhat");
   if (what1 != NULL && what2 != NULL) {
      for (i = 0; mt_get_type(what1[i].mt) != lEndT; i++) {
         if (what1[i].nm == nm) {
            if (what1[i].ep != NULL) {
               lFreeWhat(&(what1[i].ep));
            }
            what1[i].ep = *what2;
            *what2 = NULL; 
            ret = 0;
            break;
         } 
      }
      lFreeWhat(what2);
   }
   DRETURN(ret);
}



