
#include "z3950parser.h"

void Z_AttributeList_parse(bend_search_rr *rr, Z_AttributeList *Z, text_t &GSQuery)
{
  /*
    struct Z_AttributeList {
    int num_attributes;
    Z_AttributeElement **attributes;
    };
  */

  /*
    struct Z_AttributeElement {
    Z_AttributeSetId *attributeSet; // OPT
    int *attributeType;
    int which;
    union {
    int *numeric;
    Z_ComplexAttribute *complex;
    #define Z_AttributeValue_numeric 1
    #define Z_AttributeValue_complex 2
    } value;
    };
  */

  

}

void Z_Term_parse(bend_search_rr *rr, Z_Term *Z, text_t &GSQuery)
{
  /*
    struct Z_Term {
    int which;
    union {
    Odr_oct *general;
    int *numeric;
    Z_InternationalString *characterString;
    Odr_oid *oid;
    char *dateTime;
    Z_External *external;
    Z_IntUnit *integerAndUnit;
    Odr_null *null;
    #define Z_Term_general 1
    #define Z_Term_numeric 2
    #define Z_Term_characterString 3
    #define Z_Term_oid 4
    #define Z_Term_dateTime 5
    #define Z_Term_external 6
    #define Z_Term_integerAndUnit 7
    #define Z_Term_null 8
    } u;
    };
  */

  char *term;

  switch(Z->which){
  case Z_Term_general: {
    // must copy no more than len bytes from buf, or else previous query terms may be included
    int buflen = Z->u.general->len;
    term = new char[buflen+1];
    memset(term, '\0', buflen+1);
    strncpy(term, (const char*)(Z->u.general->buf), buflen);
    GSQuery += term;
    break; }
  case Z_Term_numeric:
    GSQuery += *(Z->u.numeric); // int*
    break;
  case Z_Term_characterString:
    GSQuery += Z->u.characterString; // what is this type?
    break;
  case Z_Term_oid:
    GSQuery += *(Z->u.oid); // int*
    break;
  case Z_Term_dateTime:
    GSQuery += Z->u.dateTime; // char*
    break;
  case Z_Term_external:
    GSQuery += " "; // ignore this for now
    break;
  case Z_Term_integerAndUnit:
    GSQuery += " "; // ignore this for now
    break;
  case Z_Term_null:
    GSQuery += " "; // void
    break;
  default:
    cerr << "Error in Z_Term_parse" << endl;
    exit(1);
  }
}

void Z_ProximityOperator_parse(bend_search_rr *rr, Z_ProximityOperator *Z, text_t &GSQuery)
{
  /*
    struct Z_ProximityOperator {
    bool_t *exclusion; // OPT
    int *distance;
    bool_t *ordered;
    #define Z_ProximityOperator_Prox_lessThan 1
    #define Z_ProximityOperator_Prox_lessThanOrEqual 2
    #define Z_ProximityOperator_Prox_equal 3
    #define Z_ProximityOperator_Prox_greaterThanOrEqual 4
    #define Z_ProximityOperator_Prox_greaterThan 5
    #define Z_ProximityOperator_Prox_notEqual 6
    int *relationType;
    int which;
    union {
    Z_ProxUnit *known;
    int *zprivate;
    #define Z_ProximityOperator_known 1
    #define Z_ProximityOperator_private 2
    } u;
    };
    
    #define Z_ProxUnit_character 1
    #define Z_ProxUnit_word 2
    #define Z_ProxUnit_sentence 3
    #define Z_ProxUnit_paragraph 4
    #define Z_ProxUnit_section 5
    #define Z_ProxUnit_chapter 6
    #define Z_ProxUnit_document 7
    #define Z_ProxUnit_element 8
    #define Z_ProxUnit_subelement 9
    #define Z_ProxUnit_elementType 10
    #define Z_ProxUnit_byte 11
  */

  // Greenstone (actually mgpp) supports proximity, but only 
  // to n words either side.
  // A more complete implementation of proximity can be added
  // by parsing the other fields in Z_ProximityOperator.
  // Note: this only works properly with MGPP collections
  
  GSQuery += " NEAR"; 
  GSQuery += *(Z->distance);
  GSQuery += " ";
}

void Z_Operator_parse(bend_search_rr *rr, Z_Operator *Z, text_t &GSQuery)
{
  /*
    struct Z_Operator {
    int which;
    union {
    Odr_null *and;
    Odr_null *or;
    Odr_null *and_not;
    Z_ProximityOperator *prox;
    #define Z_Operator_and 1
    #define Z_Operator_or 2
    #define Z_Operator_and_not 3
    #define Z_Operator_prox 4
    } u;
    };
  */
  
  switch(Z->which){
  case Z_Operator_and:
    GSQuery += " & ";
    break;
  case Z_Operator_or:
    GSQuery += " | ";
    break;
  case Z_Operator_and_not:
    GSQuery += " ! ";
    break;
  case Z_Operator_prox:
    Z_ProximityOperator_parse(rr, Z->u.prox, GSQuery);
    break;
  default:
    cerr << "Error in Z_Operator_parse" << endl;
    exit(1);
  }
}

void Z_Operand_parse(bend_search_rr *rr, Z_Operand *Z, text_t &GSQuery)
{
    /*
      struct Z_Operand {
      int which;
      union {
      Z_AttributesPlusTerm *attributesPlusTerm;
      Z_ResultSetId *resultSetId;
      Z_ResultSetPlusAttributes *resultAttr;
      #define Z_Operand_APT 1
      #define Z_Operand_resultSetId 2
      #define Z_Operand_resultAttr 3
      } u;
      };
    */
  
  switch(Z->which) {
  case Z_Operand_APT: {
    /*
      struct Z_AttributesPlusTerm {
      Z_AttributeList *attributes;
      Z_Term *term;
      };
    */
    
    /*
      struct Z_AttributeList {
      int num_attributes;
      Z_AttributeElement **attributes;
      };
    */
    
    /*
      struct Z_AttributeElement {
      Z_AttributeSetId *attributeSet; // OPT
      int *attributeType;
      int which;
      union {
      int *numeric;
      Z_ComplexAttribute *complex;
      #define Z_AttributeValue_numeric 1
      #define Z_AttributeValue_complex 2
      } value;
      };
    */
    
    char *term;
    
    if (Z->u.attributesPlusTerm->attributes->num_attributes > 0) {
      int Bib1_relation = *(Z->u.attributesPlusTerm->attributes->attributes[0]->attributeType);
      int Bib1_use = *(Z->u.attributesPlusTerm->attributes->attributes[0]->value.numeric);

      // if use value is 1010 ("Body of text"), is not a fielded search
      if (Bib1_use == 1010) {
	// if relation value is 6 ("not"), add a "not" to GSQuery
	if (Bib1_relation == 6) {
	  GSQuery += "! ";
	}
	Z_Term_parse(rr, Z->u.attributesPlusTerm->term, GSQuery);
      }
      // for other use values, is a fielded search
      else {
	text_t Short;
	// only add [ ]: if the index exists in the collection,
	// note: only the index names in the first collection of a
	// multi-collection search are used at the moment
	if ((Collection_map[rr->basenames[0]]).getFieldArg(Bib1_use, Short)) {
	  GSQuery += "[";
	  Z_Term_parse(rr, Z->u.attributesPlusTerm->term, GSQuery);
	  GSQuery += "]";
	  GSQuery += ":";
	  GSQuery += Short;
	}
	// if it doesn't, add it as an unfielded search term
	else {
	  cerr << "[" << rr->basenames[0] << "]: failed to find field index for " <<Bib1_use<<", adding term unfielded" << endl;
	  Z_Term_parse(rr, Z->u.attributesPlusTerm->term, GSQuery);
	}
      }
    }
    else {
      Z_Term_parse(rr, Z->u.attributesPlusTerm->term, GSQuery);
    }
    break;
  }
  case Z_Operand_resultSetId: {
    GSQuery += "(";
    GSQuery += Resultsets[Z->u.resultSetId];
    GSQuery += ")";
    break; 
  }
  case Z_Operand_resultAttr:
    GSQuery += " "; // ignore this for now
    break;
  default:
    cerr << "Error in Z_Operand_parse" << endl;
    exit(1);
  } 
}

void Z_RPNStructure_parse(bend_search_rr *rr, Z_RPNStructure *Z, text_t &GSQuery)
{
  /*
    struct Z_RPNStructure {
    int which;
    union {
    Z_Operand *simple;
    Z_Complex *complex;
    #define Z_RPNStructure_simple 1
    #define Z_RPNStructure_complex 2
    } u;
    };
  */

  switch(Z->which){
  case Z_RPNStructure_simple:
    Z_Operand_parse(rr, Z->u.simple, GSQuery);
    break;
  case Z_RPNStructure_complex:
    GSQuery += "(";
    Z_RPNStructure_parse(rr, Z->u.complex->s1, GSQuery);
    Z_Operator_parse(rr, Z->u.complex->roperator, GSQuery);
    Z_RPNStructure_parse(rr, Z->u.complex->s2, GSQuery);
    GSQuery += ")";
    break;
  default:
    cerr << "Error in Z_RPNStructure_parse" << endl;
    exit(1);
  }
}

text_t ZQueryToGSQuery(bend_search_rr *rr)
{
  text_t GSQuery;

  switch(rr->query->which){
  case Z_Query_type_0:
    cerr << "querytype = 0" << endl;
    // this is a void*, pretend it's a Greenstone-type query
    GSQuery += (char*)(rr->query->u.type_0);
    break;
  case Z_Query_type_1: {
    cerr << "querytype = 1" << endl;
    /*
      struct Z_RPNQuery {
      Z_AttributeSetId *attributeSetId;
      Z_RPNStructure *RPNStructure;
      };
    */
    // parse attributeSetId
    cerr << "attributeSetId = ";
    int c, d = 0;
    do {
      c = rr->query->u.type_1->attributeSetId[d];
      cerr << c;
      d++;
    } while (c >= 0);
    cerr << endl;

    Z_RPNStructure_parse(rr, rr->query->u.type_1->RPNStructure, GSQuery);
    break; }
  case Z_Query_type_2: {
    cerr << "querytype = 2" << endl;
    // this is just a buf (unsigned char*), pretend it's a Greenstone-type query
    GSQuery += (char*)(rr->query->u.type_2->buf);
    break; }
  case Z_Query_type_100: {
    cerr << "querytype = 100" << endl;
     // this is just a buf (unsigned char*), pretend it's a Greenstone-type query
    GSQuery += (char*)(rr->query->u.type_100->buf);
    break; }
  case Z_Query_type_101: {
    cerr << "querytype = 101" << endl;
    // check: is this really the same as a type 1 query?
    Z_RPNStructure_parse(rr, rr->query->u.type_101->RPNStructure, GSQuery);
    break; }
  case Z_Query_type_102: {
    cerr << "querytype = 102" << endl;
     // this is just a buf (unsigned char*), pretend it's a Greenstone-type query
    GSQuery += (char*)(rr->query->u.type_102->buf);
    break; }
  default: {
    cerr << "Error in ZQueryToGSQuery" << endl;
    exit(1); }
  }

  // override GSQuery (test code)
  //GSQuery = "[the]:TX";

  cerr << "GSQuery = `" << GSQuery << "'" << endl;
  return GSQuery;
}

