/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.activemq.artemis.utils;

import java.util.ArrayList;
import java.util.List;

/**
 * This class converts a JMS selector expression into an ActiveMQ Artemis core filter expression.
 *
 * JMS selector and ActiveMQ Artemis filters use the same syntax but have different identifiers.
 *
 * We basically just need to replace the JMS header and property Identifier names
 * with the corresponding ActiveMQ Artemis field and header Identifier names.
 *
 * We must be careful not to substitute any literals, or identifiers whose name contains the name
 * of one we want to substitute.
 *
 * This makes it less trivial than a simple search and replace.
 */
public class SelectorTranslator {

   public static String convertToActiveMQFilterString(final String selectorString) {
      if (selectorString == null) {
         return null;
      }

      // First convert any JMS header identifiers

      String filterString = SelectorTranslator.parse(selectorString, "JMSDeliveryMode", "AMQDurable");
      filterString = SelectorTranslator.parse(filterString, "'PERSISTENT'", "'DURABLE'");
      filterString = SelectorTranslator.parse(filterString, "'NON_PERSISTENT'", "'NON_DURABLE'");
      filterString = SelectorTranslator.parse(filterString, "JMSPriority", "AMQPriority");
      filterString = SelectorTranslator.parse(filterString, "JMSTimestamp", "AMQTimestamp");
      filterString = SelectorTranslator.parse(filterString, "JMSMessageID", "AMQUserID");
      filterString = SelectorTranslator.parse(filterString, "JMSExpiration", "AMQExpiration");

      return filterString;

   }

   private static String parse(final String input, final String match, final String replace) {
      final char quote = '\'';

      boolean inQuote = false;

      int matchPos = 0;

      List<Integer> positions = new ArrayList<>();

      boolean replaceInQuotes = match.charAt(0) == quote;

      for (int i = 0; i < input.length(); i++) {
         char c = input.charAt(i);

         if (c == quote) {
            inQuote = !inQuote;
         }

         if ((!inQuote || replaceInQuotes) && c == match.charAt(matchPos)) {
            matchPos++;

            if (matchPos == match.length()) {

               boolean matched = true;

               // Check that name is not part of another identifier name

               // Check character after match
               if (i < input.length() - 1 && Character.isJavaIdentifierPart(input.charAt(i + 1))) {
                  matched = false;
               }

               // Check character before match
               int posBeforeStart = i - match.length();

               if (posBeforeStart >= 0 && Character.isJavaIdentifierPart(input.charAt(posBeforeStart))) {
                  matched = false;
               }

               if (matched) {
                  positions.add(i - match.length() + 1);
               }

               // check previous character too

               matchPos = 0;
            }
         } else {
            matchPos = 0;
         }
      }

      if (!positions.isEmpty()) {
         StringBuffer buff = new StringBuffer();

         int startPos = 0;

         for (int pos : positions) {
            String substr = input.substring(startPos, pos);

            buff.append(substr);

            buff.append(replace);

            startPos = pos + match.length();
         }

         if (startPos < input.length()) {
            buff.append(input.substring(startPos, input.length()));
         }

         return buff.toString();
      } else {
         return input;
      }
   }
}
