/**
   * Assume that m_xpi.name == search_match.
   *
   * @return the parsed OneListSearchMatchImpl or null if the match didn't have a position
   */
  static OneListSearchMatchImpl parse_search_match(MC2Parser mc2p)
      throws MC2ParserException, IOException {

    /*
     * xml 2.3.0
    <!ELEMENT search_match ( name, itemid,
        location_name, lat?, lon?, category_list?,
        search_area*, detail_field* )>
    <!ATTLIST search_match search_match_type %search_match_type_t; #REQUIRED
        category_image CDATA #REQUIRED
        provider_image CDATA #REQUIRED
        brand_image CDATA #REQUIRED >

    <!ENTITY % search_match_type_t "(street|pointofinterest|misc|person|
        other)">
    */

    mc2p.nameOrError(MC2Strings.tsearch_match);

    String matchID = null;
    String matchLocation = null;
    String matchName = null;

    // lat, lon are optional but we skip matches without position
    // in the future these will be filtered out by the server.
    Position position = null;

    // <!ATTLIST search_match search_match_type %search_match_type_t; #REQUIRED
    //           category_image CDATA #REQUIRED
    //           provider_image CDATA #REQUIRED
    //           brand_image CDATA #REQUIRED
    //           additional_info_exists %bool; #REQUIRED >

    // XML spec for search_match attributes
    //   Images are without file extension and are empty if there is no such
    //   image for the search_match.
    final String brandImageName = mc2p.attribute(MC2Strings.abrand_image);
    final String categoryImageName = mc2p.attribute(MC2Strings.acategory_image);
    final String providerImageName = mc2p.attribute(MC2Strings.aprovider_image);
    final boolean additionalInfoExists =
        mc2p.attributeAsBoolean(MC2Strings.aadditional_info_exists);

    /*
    <!ELEMENT search_match ( name, itemid,
            location_name, lat?, lon?,
            category_list?,     //skipped
            search_area*,       //skipped
            detail_item? )>     //included if include_detail_fields is true
     */

    // there is at least 3 subelements
    mc2p.childrenOrError();

    // create this only if we expect info fields
    PoiDetailImpl detail = null;

    do {
      if (mc2p.nameRefEq(MC2Strings.tname)) { // FIXME: MC2Strings
        matchName = mc2p.value();
      } else if (mc2p.nameRefEq(MC2Strings.titemid)) {
        matchID = mc2p.value();
      } else if (mc2p.nameRefEq(MC2Strings.tlat)) {
        int lat = mc2p.valueAsInt();
        if (mc2p.advance() && mc2p.nameRefEq(MC2Strings.tlon)) {
          int lon = mc2p.valueAsInt();
          position = new Position(lat, lon);
        }
      } else if (mc2p.nameRefEq(MC2Strings.tlocation_name)) {
        matchLocation = mc2p.value();
      } else if (mc2p.nameRefEq(MC2Strings.tdetail_item)) {
        detail = mc2p.parseDetailItem();
      }
    } while (mc2p.advance());

    mc2p.nameOrError(MC2Strings.tsearch_match);

    if (position == null) {
      return null;
    } else if (matchID == null || matchLocation == null || matchName == null) {
      throw new MC2ParserException("Invalid data for search_match");
    } else {
      return new OneListSearchMatchImpl(
          matchID,
          matchLocation,
          matchName,
          position,
          brandImageName,
          categoryImageName,
          providerImageName,
          detail,
          additionalInfoExists);
    }
  }
  /* (non-Javadoc)
   * @see MC2Request#parse(MC2Parser)
   */
  public void parse(MC2Parser mc2p) throws MC2ParserException, IOException {
    mc2p.nameOrError(MC2Strings.tone_search_reply);
    /*
     * xml 2.3.0
    <!ELEMENT one_search_reply ( search_list |
            ( status_code, status_message,
            status_uri? ) ) >
    <!ATTLIST one_search_reply transaction_id ID #REQUIRED >

    <!ELEMENT search_list ( search_match* )>
    <!ATTLIST search_list number_matches %number; #REQUIRED
            total_number_matches %number; #REQUIRED >
     */
    if (LOG.isDebug()) {
      LOG.debug("OneListSearchMC2Request.parse()", "Parsing compact search reply");
    }

    mc2p.childrenOrError();

    // check and report status code
    ServerError status = mc2p.getErrorIfExists();
    if (status != null) {
      error(status);
    } else {
      mc2p.nameOrError(MC2Strings.tsearch_list);
      int nbr = mc2p.attributeAsInt(MC2Strings.anumber_matches);
      int estimatedTotal = mc2p.attributeAsInt(MC2Strings.atotal_number_matches);

      OneListSearchMatchImpl[] matches;
      if (nbr > 0) {
        if (nbr <= m_maxNbrMatches) {
          mc2p.childrenOrError();
          matches = new OneListSearchMatchImpl[nbr];
          int n = 0;
          do {
            if (n < nbr) {
              OneListSearchMatchImpl match = parse_search_match(mc2p);
              if (match != null) {
                matches[n++] = match;
              } else {
                if (LOG.isInfo()) {
                  LOG.info("OneListSearchMC2Request.parse()", "match without position, ignored");
                }
              }
            } else {
              if (LOG.isWarn()) {
                LOG.warn(
                    "OneListSearchMC2Request.parse()",
                    "there are more results than requested, skip them");
              }
            }
          } while (mc2p.advance());

          // cope with the matches that didn't have a position
          // in an ideal world this should never happen
          if (n < nbr) {
            // create a clean array if needed
            OneListSearchMatchImpl[] tmp = matches;
            matches = new OneListSearchMatchImpl[n];
            System.arraycopy(tmp, 0, matches, 0, n);

            // adjust the total number as we have ignored some result
            estimatedTotal -= nbr - n;
          }
        } else {
          throw new MC2ParserException(
              "Nbr " + nbr + " of matches bigegr than max requested " + m_maxNbrMatches);
        }
      } else {
        // if no result create an empty array
        matches = EMPY_MATCH_ARRAY;
      }
      // skip all other elements and go back to parent;
      // it should be only one call of advance()
      mc2p.advance();
      m_listener.replyReceived(estimatedTotal, matches);
    }
    mc2p.nameOrError(MC2Strings.tone_search_reply);
  }