// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2015, International Business Machines
* Corporation and others.  All Rights Reserved.
*******************************************************************************
* affixpatternparser.h
*
* created on: 2015jan06
* created by: Travis Keep
*/

#ifndef __AFFIX_PATTERN_PARSER_H__
#define __AFFIX_PATTERN_PARSER_H__

#include "unicode/utypes.h"

#if !UCONFIG_NO_FORMATTING

#include "unicode/unistr.h"
#include "unicode/uobject.h"
#include "pluralaffix.h"

U_NAMESPACE_BEGIN

class PluralRules;
class FixedPrecision;
class DecimalFormatSymbols;

/**
 * A representation of the various forms of a particular currency according
 * to some locale and usage context.
 * 
 * Includes the symbol, ISO code form, and long form(s) of the currency name
 * for each plural variation.
 */
class U_I18N_API CurrencyAffixInfo : public UMemory {
public:
    /**
     * Symbol is \u00a4; ISO form is \u00a4\u00a4;
     *  long form is \u00a4\u00a4\u00a4.
     */
    CurrencyAffixInfo();

    const UnicodeString &getSymbol() const { return fSymbol; }
    const UnicodeString &getISO() const { return fISO; }
    const PluralAffix &getLong() const { return fLong; }
    void setSymbol(const UnicodeString &symbol) {
        fSymbol = symbol;
        fIsDefault = FALSE;
    }
    void setISO(const UnicodeString &iso) {
        fISO = iso;
        fIsDefault = FALSE;
    }
    UBool
    equals(const CurrencyAffixInfo &other) const {
        return (fSymbol == other.fSymbol)
                && (fISO == other.fISO)
                && (fLong.equals(other.fLong))
                && (fIsDefault == other.fIsDefault);
    }

    /**
     * Intializes this instance.
     *
     * @param locale the locale for the currency forms.
     * @param rules The plural rules for the locale. 
     * @param currency the null terminated, 3 character ISO code of the
     * currency. If NULL, resets this instance as if it were just created.
     * In this case, the first 2 parameters may be NULL as well.
     * @param status any error returned here.
     */
    void set(
            const char *locale, const PluralRules *rules,
            const UChar *currency, UErrorCode &status);

    /**
     * Returns true if this instance is the default. That is has no real
     * currency. For instance never initialized with set()
     * or reset with set(NULL, NULL, NULL, status).
     */
    UBool isDefault() const { return fIsDefault; }

    /**
     * Adjusts the precision used for a particular currency.
     * @param currency the null terminated, 3 character ISO code of the
     * currency.
     * @param usage the usage of the currency
     * @param precision min/max fraction digits and rounding increment
     *  adjusted.
     * @params status any error reported here.
     */
    static void adjustPrecision(
            const UChar *currency, const UCurrencyUsage usage,
            FixedPrecision &precision, UErrorCode &status);

private:
    /**
     * The symbol form of the currency.
     */
    UnicodeString fSymbol;

    /**
     * The ISO form of the currency, usually three letter abbreviation.
     */
    UnicodeString fISO;

    /**
     * The long forms of the currency keyed by plural variation.
     */
    PluralAffix fLong;

    UBool fIsDefault;

};

class AffixPatternIterator;

/**
 * A locale agnostic representation of an affix pattern.
 */
class U_I18N_API AffixPattern : public UMemory {
public:

    /**
     * The token types that can appear in an affix pattern.
     */
    enum ETokenType {
        kLiteral,
        kPercent,
        kPerMill,
        kCurrency,
        kNegative,
        kPositive
    };

    /**
     * An empty affix pattern.
     */
    AffixPattern()
            : tokens(), literals(), hasCurrencyToken(FALSE),
              hasPercentToken(FALSE), hasPermillToken(FALSE),  char32Count(0) {
    }

    /**
     * Adds a string literal to this affix pattern.
     */
    void addLiteral(const UChar *, int32_t start, int32_t len);

    /**
     * Adds a token to this affix pattern. t must not be kLiteral as
     * the addLiteral() method adds literals. 
     * @param t the token type to add
     */
    void add(ETokenType t);

    /**
     * Adds a currency token with specific count to this affix pattern.
     * @param count the token count. Used to distinguish between
     *  one, two, or three currency symbols. Note that adding a currency
     *  token with count=2 (Use ISO code) is different than adding two
     *  currency tokens each with count=1 (two currency symbols).
     */
    void addCurrency(uint8_t count);

    /**
     * Makes this instance be an empty affix pattern.
     */
    void remove();

    /**
     * Provides an iterator over the tokens in this instance.
     * @param result this is initialized to point just before the
     *   first token of this instance. Caller must call nextToken()
     *   on the iterator once it is set up to have it actually point
     *   to the first token. This first call to nextToken() will return
     *   FALSE if the AffixPattern being iterated over is empty.
     * @return result
     */
    AffixPatternIterator &iterator(AffixPatternIterator &result) const;

    /**
     * Returns TRUE if this instance has currency tokens in it.
     */
    UBool usesCurrency() const {
        return hasCurrencyToken;
    }

    UBool usesPercent() const {
        return hasPercentToken;
    }

    UBool usesPermill() const {
        return hasPermillToken;
    }

    /**
     * Returns the number of code points a string of this instance
     * would have if none of the special tokens were escaped.
     * Used to compute the padding size.
     */
    int32_t countChar32() const {
        return char32Count;
    }

    /**
     * Appends other to this instance mutating this instance in place.
     * @param other The pattern appended to the end of this one.
     * @return a reference to this instance for chaining.
     */
    AffixPattern &append(const AffixPattern &other);

    /**
     * Converts this AffixPattern back into a user string.
     * It is the inverse of parseUserAffixString.
     */
    UnicodeString &toUserString(UnicodeString &appendTo) const;

    /**
     * Converts this AffixPattern back into a string.
     * It is the inverse of parseAffixString.
     */
    UnicodeString &toString(UnicodeString &appendTo) const;

    /**
     * Parses an affix pattern string appending it to an AffixPattern.
     * Parses affix pattern strings produced from using
     * DecimalFormatPatternParser to parse a format pattern. Affix patterns
     * include the positive prefix and suffix and the negative prefix
     * and suffix. This method expects affix patterns strings to be in the
     * same format that DecimalFormatPatternParser produces. Namely special
     * characters in the affix that correspond to a field type must be
     * prefixed with an apostrophe ('). These special character sequences
     * inluce minus (-), percent (%), permile (U+2030), plus (+),
     * short currency (U+00a4), medium currency (u+00a4 * 2),
     * long currency (u+a4 * 3), and apostrophe (')
     * (apostrophe does not correspond to a field type but has to be escaped
     * because it itself is the escape character).
     * Since the expansion of these special character
     * sequences is locale dependent, these sequences are not expanded in
     * an AffixPattern instance.
     * If these special characters are not prefixed with an apostrophe in
     * the affix pattern string, then they are treated verbatim just as
     * any other character. If an apostrophe prefixes a non special
     * character in the affix pattern, the apostrophe is simply ignored.
     *
     * @param affixStr the string from DecimalFormatPatternParser
     * @param appendTo parsed result appended here.
     * @param status any error parsing returned here.
     */
    static AffixPattern &parseAffixString(
            const UnicodeString &affixStr,
            AffixPattern &appendTo,
            UErrorCode &status);

    /**
     * Parses an affix pattern string appending it to an AffixPattern.
     * Parses affix pattern strings as the user would supply them.
     * In this function, quoting makes special characters like normal
     * characters whereas in parseAffixString, quoting makes special
     * characters special.
     *
     * @param affixStr the string from the user
     * @param appendTo parsed result appended here.
     * @param status any error parsing returned here.
     */
    static AffixPattern &parseUserAffixString(
            const UnicodeString &affixStr,
            AffixPattern &appendTo,
            UErrorCode &status);

    UBool equals(const AffixPattern &other) const {
        return (tokens == other.tokens)
                && (literals == other.literals)
                && (hasCurrencyToken == other.hasCurrencyToken)
                && (hasPercentToken == other.hasPercentToken)
                && (hasPermillToken == other.hasPermillToken)
                && (char32Count == other.char32Count);
    }

private:
    /*
     * Tokens stored here. Each UChar generally stands for one token. A
     * Each token is of form 'etttttttllllllll' llllllll is the length of
     * the token and ranges from 0-255. ttttttt is the token type and ranges
     * from 0-127. If e is set it means this is an extendo token (to be
     * described later). To accomodate token lengths above 255, each normal
     * token (e=0) can be followed by 0 or more extendo tokens (e=1) with
     * the same type. Right now only kLiteral Tokens have extendo tokens.
     * Each extendo token provides the next 8 higher bits for the length.
     * If a kLiteral token is followed by 2 extendo tokens then, then the
     * llllllll of the next extendo token contains bits 8-15 of the length
     * and the last extendo token contains bits 16-23 of the length.
     */
    UnicodeString tokens;

    /*
     * The characters of the kLiteral tokens are concatenated together here.
     * The first characters go with the first kLiteral token, the next
     * characters go with the next kLiteral token etc.
     */
    UnicodeString literals;
    UBool hasCurrencyToken;
    UBool hasPercentToken;
    UBool hasPermillToken;
    int32_t char32Count;
    void add(ETokenType t, uint8_t count);

};

/**
 * An iterator over the tokens in an AffixPattern instance.
 */
class U_I18N_API AffixPatternIterator : public UMemory {
public:

    /**
     * Using an iterator without first calling iterator on an AffixPattern
     * instance to initialize the iterator results in
     * undefined behavior.
     */
    AffixPatternIterator() : nextLiteralIndex(0), lastLiteralLength(0), nextTokenIndex(0), tokens(NULL), literals(NULL) { }
    /**
     * Advances this iterator to the next token. Returns FALSE when there
     * are no more tokens. Calling the other methods after nextToken()
     * returns FALSE results in undefined behavior.
     */ 
    UBool nextToken();

    /**
     * Returns the type of token.
     */
    AffixPattern::ETokenType getTokenType() const;

    /**
     * For literal tokens, returns the literal string. Calling this for
     * other token types results in undefined behavior.
     * @param result replaced with a read-only alias to the literal string.
     * @return result
     */
    UnicodeString &getLiteral(UnicodeString &result) const;

    /**
     * Returns the token length. Usually 1, but for currency tokens may
     * be 2 for ISO code and 3 for long form.
     */
    int32_t getTokenLength() const;
private:
    int32_t nextLiteralIndex;
    int32_t lastLiteralLength;
    int32_t nextTokenIndex;
    const UnicodeString *tokens;
    const UnicodeString *literals;
    friend class AffixPattern;
    AffixPatternIterator(const AffixPatternIterator &);
    AffixPatternIterator &operator=(const AffixPatternIterator &);
};

/**
 * A locale aware class that converts locale independent AffixPattern
 * instances into locale dependent PluralAffix instances.
 */
class U_I18N_API AffixPatternParser : public UMemory {
public:
AffixPatternParser();
AffixPatternParser(const DecimalFormatSymbols &symbols);
void setDecimalFormatSymbols(const DecimalFormatSymbols &symbols);

/**
 * Parses affixPattern appending the result to appendTo.
 * @param affixPattern The affix pattern.
 * @param currencyAffixInfo contains the currency forms.
 * @param appendTo The result of parsing affixPattern is appended here.
 * @param status any error returned here.
 * @return appendTo.
 */
PluralAffix &parse(
        const AffixPattern &affixPattern,
        const CurrencyAffixInfo &currencyAffixInfo,
        PluralAffix &appendTo,
        UErrorCode &status) const;

UBool equals(const AffixPatternParser &other) const {
    return (fPercent == other.fPercent)
            && (fPermill == other.fPermill)
            && (fNegative == other.fNegative)
            && (fPositive == other.fPositive);
}

private:
UnicodeString fPercent;
UnicodeString fPermill;
UnicodeString fNegative;
UnicodeString fPositive;
};


U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
#endif  // __AFFIX_PATTERN_PARSER_H__
