/**
   * Construct a mc2 request that can be posted on {@link MC2Interface}
   *
   * @param listener {@link RequestListener} that get notified when the result succeed or fail,
   *     cannot be null.
   * @param query {@link SearchQuery} must be not null and of type {@link
   *     SearchQuery#SEARCH_TYPE_POSITIONAL}
   * @param round a positive number representing the search type (e.g. 0 for internal search, 1 for
   *     external providers)
   * @param sorting the sorting mode, must be one of {@link #ALFA_SORT}, {@link #DISTANCE_SORT}
   *     otherwise an IllegalArgumentException will be trown
   * @param langauge LanguageInternal for the server reply, cannot be null.
   */
  OneListSearchMC2Request(
      LanguageInternal language,
      RequestListener listener,
      SearchQuery query,
      int round,
      String sorting) {

    if (language == null || listener == null || query == null) {
      throw new IllegalArgumentException("Language, listener and query param cannot be null");
    }
    // check early for problems to not have error when writing the request
    // that will cause failure of all other merged requests
    if (query.getQueryType() != SearchQuery.SEARCH_TYPE_POSITIONAL
        && query.getQueryType() != SearchQuery.SEARCH_TYPE_ADDRESS) {
      throw new IllegalArgumentException(
          "SearchQuery parameter must be of "
              + "type SEARCH_TYPE_POSITIONAL or SEARCH_TYPE_ADDRESS");
    }

    if (query.getMaxNbrMatches() <= 0) {
      throw new IllegalArgumentException("MaxNbrMatches must be greater than 0");
    }

    if (sorting != ALFA_SORT && sorting != DISTANCE_SORT) {
      throw new IllegalArgumentException(
          "Not an accepted sorting mode, "
              + "must be either OneListSearchMC2Request.ALFA_SORT or "
              + "OneListSearchMC2Request.DISTANCE_SORT");
    }

    // Don't check that round is valid - server will handle that and we
    // don't want checks to become out of sync.

    // needed to get the current language
    m_language = language;
    m_listener = listener;
    m_query = query;
    m_round = round;
    // must be > 0
    m_maxNbrMatches = query.getMaxNbrMatches();
    m_sorting = sorting;
  }
  /* (non-Javadoc)
   * @see MC2Request#write(MC2Writer)
   */
  public final void write(MC2Writer mc2w) throws IOException {
    /*
     * xml 2.3.0
    <!ELEMENT one_search_request
        ( search_match_query?, category_list?,
        ( position_item, distance? ) |
        ( query_location, top_region_id ) ) >
    <!ATTLIST one_search_request transaction_id ID #REQUIRED
        max_number_matches %number; #REQUIRED
        language %language_t; #REQUIRED
        round %number; #REQUIRED
        version %number; #REQUIRED
        include_info_fields %bool; #IMPLIED
        position_system %position_system_t; "MC2"
        sorting %sorting_t; #REQUIRED
        search_type %search_for_type_t; "all">
    <!ENTITY % sorting_t "(alfa_sort|distance_sort)">
    <!ENTITY % search_for_type_t "(address|all)">
     */
    if (LOG.isDebug()) {
      LOG.debug("OneListSearchMC2Request.write()", "Writing compact search request");
    }
    // writer is positioned in inside one_search_request and the
    // transaction_id has been written

    mc2w.attribute(MC2Strings.aversion, SERVER_VERSION);
    // language
    mc2w.attribute(MC2Strings.alanguage, m_language.getXMLCode());

    mc2w.attribute(MC2Strings.amax_number_matches, m_maxNbrMatches);
    mc2w.attribute(MC2Strings.around, m_round);
    mc2w.attribute(MC2Strings.asorting, m_sorting);
    mc2w.attribute(MC2Strings.ainclude_detail_fields, m_query.includeDetails());
    if (m_query.getQueryType() == SearchQuery.SEARCH_TYPE_ADDRESS) {
      mc2w.attribute(MC2Strings.asearch_type, SEARCH_ADDRESS);
    }

    String query = m_query.getItemQueryStr();
    if (query != null) {
      /*<!ELEMENT search_match_query ( #PCDATA )>*/
      mc2w.elementWithText(MC2Strings.tsearch_match_query, query);
    }
    Category category = m_query.getCategory();
    if (category != null) {
      // even there can only be a single category we still have to start a list
      /*<!ELEMENT category_list ( category_id+ ) >
       <!ELEMENT category_id ( #PCDATA ) >
      */
      mc2w.startElement(MC2Strings.tcategory_list);
      mc2w.elementWithText(MC2Strings.tcategory_id, category.getCategoryID());
      mc2w.endElement(MC2Strings.tcategory_list);
    }

    if (m_query.getQueryType() == SearchQuery.SEARCH_TYPE_POSITIONAL) {
      /*<!ELEMENT position_item ( lat, lon, angle? )>
      <!ATTLIST position_item position_system %position_system_t; #REQUIRED>*/
      // position is a must
      m_query.getPosition().write(mc2w);

      int distance = m_query.getSearchRadius();
      if (distance != SearchQuery.RADIUS_SERVER_DEFAULT) {
        /*<!ELEMENT distance ( #PCDATA )>*/
        mc2w.elementWithText(MC2Strings.tdistance, distance);
      }
    } else if (m_query.getQueryType() == SearchQuery.SEARCH_TYPE_ADDRESS) {
      String where =
          (m_query.getSearchAreaStr() != null
              ? m_query.getSearchAreaStr()
              : MC2Strings.EMPTY_STRING);
      mc2w.elementWithText(MC2Strings.tquery_location, where);
      mc2w.elementWithText(
          MC2Strings.ttop_region_id, ((TopRegionImpl) m_query.getTopRegion()).getRegionID());
    }
  }