// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.rt.execution.junit;


import java.util.Iterator;
import java.util.Map;

public final class MapSerializerUtil {
  public static final String TEST_FAILED = "testFailed";
  public static final String TEST_IGNORED = "testIgnored";


  /**
   * String escaping info provider.
   */
  public interface EscapeInfoProvider {
    /**
     * Converts character to its representation in the final string
     * @param c character to convert
     * @return character representation or 0 if conversion is not applicable to that character
     */
    char escape(char c);

    /**
     * Escape character to use before escaped characters (before character representations generated by {@link #escape(char)}  method)
     * @return see above
     */
    char escapeCharacter();
  }

  public static final EscapeInfoProvider STD_ESCAPER = new EscapeInfoProvider() {
    @Override
    public char escape(final char c) {
      switch (c) {
        case '\n': return 'n';
        case '\r': return 'r';
        case '\b': return 'b';
        case '\u0085': return 'x'; // next-line character
        case '\u2028': return 'l'; // line-separator character
        case '\u2029': return 'p'; // paragraph-separator character
        case '|': return '|';
        case '\'': return '\'';
        case '[': return '[';
        case ']': return ']';
        default:return 0;
      }
    }

    @Override
    public char escapeCharacter() {
      return '|';
    }
  };

  /**
   * Escapes characters specified by provider with '\' and specified character.
   * @param str initial string
   * @param p escape info provider.
   * @return escaped string.
   */
  public static String escapeStr(final String str, EscapeInfoProvider p) {
    if (str == null) return null;
    int finalCount = calcFinalEscapedStringCount(str, p);

    if (str.length() == finalCount) return str;

    char[] resultChars = new char[finalCount];
    int resultPos = 0;
    for (int i = 0; i < str.length(); i++) {
      char c = str.charAt(i);
      final char escaped = p.escape(c);
      if (escaped != 0) {
        resultChars[resultPos++] = p.escapeCharacter();
        resultChars[resultPos++] = escaped;
      }
      else {
        resultChars[resultPos++] = c;
      }
    }

    if (resultPos != finalCount) {
      throw new RuntimeException("Incorrect escaping for '" + str + "'");
    }
    return new String(resultChars);
  }

  private static int calcFinalEscapedStringCount(final String name, final EscapeInfoProvider p) {
    int result = 0;
    for (int i = 0; i < name.length(); i++) {
      char c = name.charAt(i);
      if (p.escape(c) != 0) {
        result += 2;
      }
      else {
        result += 1;
      }
    }

    return result;
  }

  public static String asString(final String messageName, final Map attributes) {
    String text = "##teamcity[" + messageName;
    for (Iterator iterator = attributes.keySet().iterator(); iterator.hasNext(); ) {
      final Object attrName = iterator.next();
      text += " " + attrName + "='" + escapeStr((String)attributes.get(attrName), STD_ESCAPER) + "'";
    }
    text += "]";
    return text;
  }

}
