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

#include "unicode/utypes.h"

#if !UCONFIG_NO_FORMATTING

#include "unicode/uformattedvalue.h"
#include "unicode/unum.h"
#include "unicode/ustring.h"
#include "cformtst.h"
#include "cintltst.h"
#include "cmemory.h"
#include "cstring.h"
#include "uassert.h"

static void TestBasic(void);
static void TestSetters(void);

static void AssertAllPartsEqual(
    const char* messagePrefix,
    const UConstrainedFieldPosition* ucfpos,
    int32_t matching,
    UFieldCategory category,
    int32_t field,
    int32_t start,
    int32_t limit,
    int64_t context);

void addUFormattedValueTest(TestNode** root);

#define TESTCASE(x) addTest(root, &x, "tsformat/uformattedvalue/" #x)

void addUFormattedValueTest(TestNode** root) {
    TESTCASE(TestBasic);
    TESTCASE(TestSetters);
}


static void TestBasic() {
    UErrorCode status = U_ZERO_ERROR;
    UConstrainedFieldPosition* ucfpos = ucfpos_open(&status);
    assertSuccess("opening ucfpos", &status);
    assertTrue("ucfpos should not be null", ucfpos != NULL);

    AssertAllPartsEqual(
        "basic",
        ucfpos,
        7,
        UFIELD_CATEGORY_UNDEFINED,
        0,
        0,
        0,
        0LL);

    ucfpos_close(ucfpos);
}

void TestSetters() {
    UErrorCode status = U_ZERO_ERROR;
    UConstrainedFieldPosition* ucfpos = ucfpos_open(&status);
    assertSuccess("opening ucfpos", &status);
    assertTrue("ucfpos should not be null", ucfpos != NULL);

    ucfpos_constrainCategory(ucfpos, UFIELD_CATEGORY_DATE, &status);
    assertSuccess("setters 0", &status);
    AssertAllPartsEqual(
        "setters 0",
        ucfpos,
        4,
        UFIELD_CATEGORY_DATE,
        0,
        0,
        0,
        0LL);

    ucfpos_constrainField(ucfpos, UFIELD_CATEGORY_NUMBER, UNUM_COMPACT_FIELD, &status);
    assertSuccess("setters 1", &status);
    AssertAllPartsEqual(
        "setters 1",
        ucfpos,
        2,
        UFIELD_CATEGORY_NUMBER,
        UNUM_COMPACT_FIELD,
        0,
        0,
        0LL);

    ucfpos_setInt64IterationContext(ucfpos, 42424242424242LL, &status);
    assertSuccess("setters 2", &status);
    AssertAllPartsEqual(
        "setters 2",
        ucfpos,
        2,
        UFIELD_CATEGORY_NUMBER,
        UNUM_COMPACT_FIELD,
        0,
        0,
        42424242424242LL);

    ucfpos_setState(ucfpos, UFIELD_CATEGORY_NUMBER, UNUM_COMPACT_FIELD, 5, 10, &status);
    assertSuccess("setters 3", &status);
    AssertAllPartsEqual(
        "setters 3",
        ucfpos,
        2,
        UFIELD_CATEGORY_NUMBER,
        UNUM_COMPACT_FIELD,
        5,
        10,
        42424242424242LL);

    ucfpos_reset(ucfpos, &status);
    assertSuccess("setters 4", &status);
    AssertAllPartsEqual(
        "setters 4",
        ucfpos,
        7,
        UFIELD_CATEGORY_UNDEFINED,
        0,
        0,
        0,
        0LL);

    ucfpos_close(ucfpos);
}

/** For matching, turn on these bits:
 *
 * 1 = UNUM_INTEGER_FIELD
 * 2 = UNUM_COMPACT_FIELD
 * 4 = UDAT_AM_PM_FIELD
 */
static void AssertAllPartsEqual(
        const char* messagePrefix,
        const UConstrainedFieldPosition* ucfpos,
        int32_t matching,
        UFieldCategory category,
        int32_t field,
        int32_t start,
        int32_t limit,
        int64_t context) {

    UErrorCode status = U_ZERO_ERROR;

    char message[256];
    uprv_strncpy(message, messagePrefix, 256);
    int32_t prefixEnd = (int32_t)uprv_strlen(messagePrefix);
    message[prefixEnd++] = ':';
    message[prefixEnd++] = ' ';
    U_ASSERT(prefixEnd < 256);

#define AAPE_MSG(suffix) (uprv_strncpy(message+prefixEnd, suffix, 256-prefixEnd)-prefixEnd)

    UFieldCategory _category = ucfpos_getCategory(ucfpos, &status);
    assertSuccess(AAPE_MSG("_"), &status);
    assertIntEquals(AAPE_MSG("category"), category, _category);

    int32_t _field = ucfpos_getField(ucfpos, &status);
    assertSuccess(AAPE_MSG("field"), &status);
    assertIntEquals(AAPE_MSG("field"), field, _field);

    int32_t _start, _limit;
    ucfpos_getIndexes(ucfpos, &_start, &_limit, &status);
    assertSuccess(AAPE_MSG("indexes"), &status);
    assertIntEquals(AAPE_MSG("start"), start, _start);
    assertIntEquals(AAPE_MSG("limit"), limit, _limit);

    int64_t _context = ucfpos_getInt64IterationContext(ucfpos, &status);
    assertSuccess(AAPE_MSG("context"), &status);
    assertIntEquals(AAPE_MSG("context"), context, _context);

    UBool _matchesInteger = ucfpos_matchesField(ucfpos, UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD, &status);
    assertSuccess(AAPE_MSG("integer field"), &status);
    assertTrue(AAPE_MSG("integer field"),
        ((matching & 1) != 0) ? _matchesInteger : !_matchesInteger);

    UBool _matchesCompact = ucfpos_matchesField(ucfpos, UFIELD_CATEGORY_NUMBER, UNUM_COMPACT_FIELD, &status);
    assertSuccess(AAPE_MSG("compact field"), &status);
    assertTrue(AAPE_MSG("compact field"),
        ((matching & 2) != 0) ? _matchesCompact : !_matchesCompact);

    UBool _matchesDate = ucfpos_matchesField(ucfpos, UFIELD_CATEGORY_DATE, UDAT_AM_PM_FIELD, &status);
    assertSuccess(AAPE_MSG("date field"), &status);
    assertTrue(AAPE_MSG("date field"),
        ((matching & 4) != 0) ? _matchesDate : !_matchesDate);
}


static void checkFormattedValueString(
        const char* message,
        const UFormattedValue* fv,
        const UChar* expectedString,
        UErrorCode* ec) {
    int32_t length;
    const UChar* actualString = ufmtval_getString(fv, &length, ec);
    if (U_FAILURE(*ec)) {
        assertIntEquals(message, 0, length);
        return;
    }
    assertSuccess(message, ec);
    // The string is guaranteed to be NUL-terminated.
    int32_t actualLength = u_strlen(actualString);
    assertIntEquals(message, actualLength, length);
    assertUEquals(message, expectedString, actualString);
}

// Declared in cformtst.h
void checkFormattedValue(
        const char* message,
        const UFormattedValue* fv,
        const UChar* expectedString,
        UFieldCategory expectedCategory,
        const UFieldPosition* expectedFieldPositions,
        int32_t expectedFieldPositionsLength) {
    (void)expectedFieldPositionsLength; // suppress compiler warnings about unused variable
    UErrorCode ec = U_ZERO_ERROR;
    checkFormattedValueString(message, fv, expectedString, &ec);
    if (U_FAILURE(ec)) { return; }

    // Basic loop over the fields (more rigorous testing in C++)
    UConstrainedFieldPosition* ucfpos = ucfpos_open(&ec);
    int32_t i = 0;
    while (ufmtval_nextPosition(fv, ucfpos, &ec)) {
        assertIntEquals("category",
            expectedCategory, ucfpos_getCategory(ucfpos, &ec));
        assertIntEquals("field",
            expectedFieldPositions[i].field, ucfpos_getField(ucfpos, &ec));
        int32_t start, limit;
        ucfpos_getIndexes(ucfpos, &start, &limit, &ec);
        assertIntEquals("start",
            expectedFieldPositions[i].beginIndex, start);
        assertIntEquals("limit",
            expectedFieldPositions[i].endIndex, limit);
        i++;
    }
    assertTrue("After loop", !ufmtval_nextPosition(fv, ucfpos, &ec));
    assertSuccess("After loop", &ec);
    ucfpos_close(ucfpos);
}

void checkMixedFormattedValue(
        const char* message,
        const UFormattedValue* fv,
        const UChar* expectedString,
        const UFieldPositionWithCategory* expectedFieldPositions,
        int32_t length) {
    (void)length; // suppress compiler warnings about unused variable
    UErrorCode ec = U_ZERO_ERROR;
    checkFormattedValueString(message, fv, expectedString, &ec);
    if (U_FAILURE(ec)) { return; }

    // Basic loop over the fields (more rigorous testing in C++)
    UConstrainedFieldPosition* ucfpos = ucfpos_open(&ec);
    int32_t i = 0;
    while (ufmtval_nextPosition(fv, ucfpos, &ec)) {
        assertIntEquals("category",
            expectedFieldPositions[i].category, ucfpos_getCategory(ucfpos, &ec));
        assertIntEquals("field",
            expectedFieldPositions[i].field, ucfpos_getField(ucfpos, &ec));
        int32_t start, limit;
        ucfpos_getIndexes(ucfpos, &start, &limit, &ec);
        assertIntEquals("start",
            expectedFieldPositions[i].beginIndex, start);
        assertIntEquals("limit",
            expectedFieldPositions[i].endIndex, limit);
        i++;
    }
    assertTrue("After loop", !ufmtval_nextPosition(fv, ucfpos, &ec));
    assertSuccess("After loop", &ec);
    ucfpos_close(ucfpos);
}


#endif /* #if !UCONFIG_NO_FORMATTING */
