// © 2024 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html

#include "unicode/utypes.h"

#if !UCONFIG_NO_NORMALIZATION

#if !UCONFIG_NO_FORMATTING

#if !UCONFIG_NO_MF2

#include "unicode/messageformat2_data_model.h"
#include "messageformat2_macros.h"
#include "messageformat2_serializer.h"
#include "uvector.h" // U_ASSERT

U_NAMESPACE_BEGIN

namespace message2 {

// Generates a string representation of a data model
// ------------------------------------------------

using namespace data_model;

// Private helper methods

void Serializer::whitespace() {
    result += SPACE;
}

void Serializer::emit(UChar32 c) {
    result += c;
}

void Serializer::emit(const UnicodeString& s) {
    result += s;
}

void Serializer::emit(const std::u16string_view& token) {
    result.append(token);
}

void Serializer::emit(const Literal& l) {
    if (l.isQuoted()) {
      emit(PIPE);
    }
    const UnicodeString& contents = l.unquoted();
    for (int32_t i = 0; ((int32_t) i) < contents.length(); i++) {
        // Re-escape any escaped-char characters
        switch(contents[i]) {
        case BACKSLASH:
        case PIPE:
        case LEFT_CURLY_BRACE:
        case RIGHT_CURLY_BRACE: {
            emit(BACKSLASH);
            break;
        }
        default: {
            break;
        }
        }
        emit(contents[i]);
    }
    if (l.isQuoted()) {
        emit(PIPE);
    }
}

void Serializer::emit(const Key& k) {
    if (k.isWildcard()) {
        emit(ASTERISK);
        return;
    }
    emit(k.asLiteral());
}

void Serializer::emit(const SelectorKeys& k) {
  const Key* ks = k.getKeysInternal();
  int32_t len = k.len;
  // It would be an error for `keys` to be empty;
  // that would mean this is the single `pattern`
  // variant, and in that case, this method shouldn't be called
  U_ASSERT(len > 0);
  for (int32_t i = 0; i < len; i++) {
    if (i != 0) {
      whitespace();
    }
    emit(ks[i]);
  }
}

void Serializer::emit(const Operand& rand) {
    U_ASSERT(!rand.isNull());

    if (rand.isVariable()) {
        emit(DOLLAR);
        emit(rand.asVariable());
    } else {
        // Literal: quoted or unquoted
        emit(rand.asLiteral());
    }
}

void Serializer::emit(const OptionMap& options) {
    // Errors should have been checked before this point
    UErrorCode localStatus = U_ZERO_ERROR;
    U_ASSERT(!options.bogus);
    for (int32_t i = 0; i < options.size(); i++) {
        const Option& opt = options.getOption(i, localStatus);
        // No need to check error code, since we already checked
        // that !bogus
        whitespace();
        emit(opt.getName());
        emit(EQUALS);
        emit(opt.getValue());
    }
}

void Serializer::emitAttributes(const OptionMap& attributes) {
    // Errors should have been checked before this point
    UErrorCode localStatus = U_ZERO_ERROR;
    U_ASSERT(!attributes.bogus);
    for (int32_t i = 0; i < attributes.size(); i++) {
        const Option& attr = attributes.getOption(i, localStatus);
        // No need to check error code, since we already checked
        // that !bogus
        whitespace();
        emit(AT);
        emit(attr.getName());
        const Operand& v = attr.getValue();
        if (!v.isNull()) {
            emit(EQUALS);
            emit(v);
        }
    }
}

 void Serializer::emit(const Expression& expr) {
    emit(LEFT_CURLY_BRACE);

    if (!expr.isFunctionCall()) {
        // Literal or variable, no annotation
        emit(expr.getOperand());
    } else {
        // Function call or reserved
        if (!expr.isStandaloneAnnotation()) {
          // Must be a function call that has an operand
          emit(expr.getOperand());
          whitespace();
        }
        UErrorCode localStatus = U_ZERO_ERROR;
        const Operator* rator = expr.getOperator(localStatus);
        U_ASSERT(U_SUCCESS(localStatus));
        emit(COLON);
        emit(rator->getFunctionName());
        // No whitespace after function name, in case it has
        // no options. (when there are options, emit(OptionMap) will
        // emit the leading whitespace)
        emit(rator->getOptionsInternal());
    }
    emitAttributes(expr.getAttributesInternal());
    emit(RIGHT_CURLY_BRACE);
}

void Serializer::emit(const PatternPart& part) {
    if (part.isText()) {
        // Raw text
        const UnicodeString& text = part.asText();
        // Re-escape '{'/'}'/'\''|'
        for (int32_t i = 0; ((int32_t) i) < text.length(); i++) {
          switch(text[i]) {
          case PIPE:
          case BACKSLASH:
          case LEFT_CURLY_BRACE:
          case RIGHT_CURLY_BRACE: {
            emit(BACKSLASH);
            break;
          }
          default:
            break;
          }
          emit(text[i]);
        }
        return;
    }
    // Markup
    if (part.isMarkup()) {
        const Markup& markup = part.asMarkup();
        emit(LEFT_CURLY_BRACE);
        if (markup.isClose()) {
            emit(SLASH);
            } else {
            emit(NUMBER_SIGN);
        }
        emit(markup.getName());
        emit(markup.getOptionsInternal());
        emitAttributes(markup.getAttributesInternal());
        if (markup.isStandalone()) {
            emit(SLASH);
        }
        emit(RIGHT_CURLY_BRACE);
        return;
    }
    // Expression
    emit(part.contents());
}

void Serializer::emit(const Pattern& pat) {
    int32_t len = pat.numParts();
    // Always quote pattern, which should match the normalized input
    // if the parser is constructing it correctly
    emit(LEFT_CURLY_BRACE);
    emit(LEFT_CURLY_BRACE);
    for (int32_t i = 0; i < len; i++) {
        // No whitespace is needed here -- see the `pattern` nonterminal in the grammar
        emit(pat.getPart(i));
    }
    emit(RIGHT_CURLY_BRACE);
    emit(RIGHT_CURLY_BRACE);
}

void Serializer::serializeDeclarations() {
    const Binding* bindings = dataModel.getLocalVariablesInternal();
    U_ASSERT(dataModel.bindingsLen == 0 || bindings != nullptr);

    for (int32_t i = 0; i < dataModel.bindingsLen; i++) {
        const Binding& b = bindings[i];
        if (b.isLocal()) {
            // No whitespace needed here -- see `message` in the grammar
            emit(ID_LOCAL);
            whitespace();
            emit(DOLLAR);
            emit(b.getVariable());
            // No whitespace needed here -- see `local-declaration` in the grammar
            emit(EQUALS);
            // No whitespace needed here -- see `local-declaration` in the grammar
        } else {
            // Input declaration
            emit(ID_INPUT);
            // No whitespace needed here -- see `input-declaration` in the grammar
        }
        emit(b.getValue());
    }
}

void Serializer::serializeSelectors() {
    U_ASSERT(!dataModel.hasPattern());
    const VariableName* selectors = dataModel.getSelectorsInternal();

    emit(ID_MATCH);
    for (int32_t i = 0; i < dataModel.numSelectors(); i++) {
        whitespace();
        emit(DOLLAR);
        emit(selectors[i]);
    }
}

void Serializer::serializeVariants() {
    U_ASSERT(!dataModel.hasPattern());
    const Variant* variants = dataModel.getVariantsInternal();
    whitespace();
    for (int32_t i = 0; i < dataModel.numVariants(); i++) {
        const Variant& v = variants[i];
        emit(v.getKeys());
        // No whitespace needed here -- see `variant` in the grammar
        emit(v.getPattern());
    }
}


// Main (public) serializer method
void Serializer::serialize() {
    serializeDeclarations();
    // Pattern message
    if (dataModel.hasPattern()) {
      emit(dataModel.getPattern());
    } else {
      // Selectors message
      serializeSelectors();
      serializeVariants();
    }
}

} // namespace message2
U_NAMESPACE_END

#endif /* #if !UCONFIG_NO_MF2 */

#endif /* #if !UCONFIG_NO_FORMATTING */

#endif /* #if !UCONFIG_NO_NORMALIZATION */
