// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
 *******************************************************************************
 * Copyright (C) 2011-2016, International Business Machines Corporation and
 * others. All Rights Reserved.
 *******************************************************************************
 */

#ifndef __TZNAMES_IMPL_H__
#define __TZNAMES_IMPL_H__


/**
 * \file
 * \brief C++ API: TimeZoneNames object
 */

#include "unicode/utypes.h"

#if !UCONFIG_NO_FORMATTING

#include "unicode/tznames.h"
#include "unicode/ures.h"
#include "unicode/locid.h"
#include "uhash.h"
#include "uvector.h"
#include "umutex.h"
#include "fixedstring.h"

// Some zone display names involving supplementary characters can be over 50 chars, 100 UTF-16 code units, 200 UTF-8 bytes
#define ZONE_NAME_U16_MAX 128

U_NAMESPACE_BEGIN

/*
 * ZNStringPool    Pool of (char16_t *) strings.  Provides for sharing of repeated
 *                 zone strings.
 */
struct ZNStringPoolChunk;
class U_I18N_API ZNStringPool: public UMemory {
  public:
    ZNStringPool(UErrorCode &status);
    ~ZNStringPool();

    /* Get the pooled string that is equal to the supplied string s.
     * Copy the string into the pool if it is not already present.
     *
     * Life time of the returned string is that of the pool.
     */
    const char16_t *get(const char16_t *s, UErrorCode &status);

    /* Get the pooled string that is equal to the supplied string s.
     * Copy the string into the pool if it is not already present.
     */
    const char16_t *get(const UnicodeString &s, UErrorCode &status);

    /* Adopt a string into the pool, without copying it.
     * Used for strings from resource bundles, which will persist without copying.
     */
    const char16_t *adopt(const char16_t *s, UErrorCode &status);

    /* Freeze the string pool.  Discards the hash table that is used
     * for looking up a string.  All pointers to pooled strings remain valid.
     */
    void freeze();

  private:
    ZNStringPoolChunk   *fChunks;
    UHashtable           *fHash;
};

/*
 * Character node used by TextTrieMap
 */
struct CharacterNode {
    // No constructor or destructor.
    // We malloc and free an uninitialized array of CharacterNode objects
    // and clear and delete them ourselves.

    void clear();
    void deleteValues(UObjectDeleter *valueDeleter);

    void addValue(void *value, UObjectDeleter *valueDeleter, UErrorCode &status);
    inline UBool hasValues() const;
    inline int32_t countValues() const;
    inline const void *getValue(int32_t index) const;

    void     *fValues;      // Union of one single value vs. UVector of values.
    char16_t fCharacter;    // UTF-16 code unit.
    uint16_t fFirstChild;   // 0 if no children.
    uint16_t fNextSibling;  // 0 terminates the list.
    UBool    fHasValuesVector;
    UBool    fPadding;

    // No value:   fValues == nullptr            and  fHasValuesVector == false
    // One value:  fValues == value              and  fHasValuesVector == false
    // >=2 values: fValues == UVector of values  and  fHasValuesVector == true
};

inline UBool CharacterNode::hasValues() const {
    return fValues != nullptr;
}

inline int32_t CharacterNode::countValues() const {
    return
        fValues == nullptr ? 0 :
        !fHasValuesVector ? 1 :
        static_cast<const UVector*>(fValues)->size();
}

inline const void *CharacterNode::getValue(int32_t index) const {
    if (!fHasValuesVector) {
        return fValues;  // Assume index == 0.
    } else {
        return static_cast<const UVector*>(fValues)->elementAt(index);
    }
}

/*
 * Search result handler callback interface used by TextTrieMap search.
 */
class TextTrieMapSearchResultHandler : public UMemory {
public:
    virtual UBool handleMatch(int32_t matchLength,
                              const CharacterNode *node, UErrorCode& status) = 0;
    virtual ~TextTrieMapSearchResultHandler(); //added to avoid warning
};

/**
 * TextTrieMap is a trie implementation for supporting
 * fast prefix match for the string key.
 */
class U_I18N_API TextTrieMap : public UMemory {
public:
    TextTrieMap(UBool ignoreCase, UObjectDeleter *valeDeleter);
    virtual ~TextTrieMap();

    void put(const UnicodeString &key, void *value, ZNStringPool &sp, UErrorCode &status);
    void put(const char16_t*, void *value, UErrorCode &status);
    void search(const UnicodeString &text, int32_t start,
        TextTrieMapSearchResultHandler *handler, UErrorCode& status) const;
    int32_t isEmpty() const;

private:
    UBool           fIgnoreCase;
    CharacterNode   *fNodes;
    int32_t         fNodesCapacity;
    int32_t         fNodesCount;

    UVector         *fLazyContents;
    UBool           fIsEmpty;
    UObjectDeleter  *fValueDeleter;

    UBool growNodes();
    CharacterNode* addChildNode(CharacterNode *parent, char16_t c, UErrorCode &status);
    CharacterNode* getChildNode(CharacterNode *parent, char16_t c) const;

    void putImpl(const UnicodeString &key, void *value, UErrorCode &status);
    void buildTrie(UErrorCode &status);
    void search(CharacterNode *node, const UnicodeString &text, int32_t start,
        int32_t index, TextTrieMapSearchResultHandler *handler, UErrorCode &status) const;
};



class ZNames;
class TextTrieMap;
class ZNameSearchHandler;

class TimeZoneNamesImpl : public TimeZoneNames {
public:
    TimeZoneNamesImpl(const Locale& locale, UErrorCode& status);

    virtual ~TimeZoneNamesImpl();

    virtual bool operator==(const TimeZoneNames& other) const override;
    virtual TimeZoneNamesImpl* clone() const override;

    StringEnumeration* getAvailableMetaZoneIDs(UErrorCode& status) const override;
    StringEnumeration* getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const override;

    UnicodeString& getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const override;
    UnicodeString& getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const override;

    UnicodeString& getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const override;
    UnicodeString& getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const override;

    UnicodeString& getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const override;

    TimeZoneNames::MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const override;

    void loadAllDisplayNames(UErrorCode& status) override;
    void getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const override;

    static UnicodeString& getDefaultExemplarLocationName(const UnicodeString& tzID, UnicodeString& name);

    static StringEnumeration* _getAvailableMetaZoneIDs(UErrorCode& status);
    static StringEnumeration* _getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status);
    static UnicodeString& _getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID);
    static UnicodeString& _getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID);

private:

    Locale fLocale;

    UResourceBundle* fZoneStrings;

    UHashtable* fTZNamesMap;
    UHashtable* fMZNamesMap;

    UBool fNamesTrieFullyLoaded;
    UBool fNamesFullyLoaded;
    TextTrieMap fNamesTrie;

    void initialize(const Locale& locale, UErrorCode& status);
    void cleanup();

    void loadStrings(const UnicodeString& tzCanonicalID, UErrorCode& status);

    ZNames* loadMetaZoneNames(const UnicodeString& mzId, UErrorCode& status);
    ZNames* loadTimeZoneNames(const UnicodeString& mzId, UErrorCode& status);
    TimeZoneNames::MatchInfoCollection* doFind(ZNameSearchHandler& handler,
        const UnicodeString& text, int32_t start, UErrorCode& status) const;
    void addAllNamesIntoTrie(UErrorCode& errorCode);

    void internalLoadAllDisplayNames(UErrorCode& status);

    struct ZoneStringsLoader;
};

class TZDBNames;

class TZDBTimeZoneNames : public TimeZoneNames {
public:
    TZDBTimeZoneNames(const Locale& locale);
    virtual ~TZDBTimeZoneNames();

    virtual bool operator==(const TimeZoneNames& other) const override;
    virtual TZDBTimeZoneNames* clone() const override;

    StringEnumeration* getAvailableMetaZoneIDs(UErrorCode& status) const override;
    StringEnumeration* getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const override;

    UnicodeString& getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const override;
    UnicodeString& getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const override;

    UnicodeString& getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const override;
    UnicodeString& getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const override;

    TimeZoneNames::MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const override;

    // When TZDBNames for the metazone is not available, this method returns nullptr,
    // but does NOT set U_MISSING_RESOURCE_ERROR to status.
    static const TZDBNames* getMetaZoneNames(const UnicodeString& mzId, UErrorCode& status);

private:
    Locale fLocale;
    FixedString fRegion;
};

U_NAMESPACE_END

#endif /* #if !UCONFIG_NO_FORMATTING */

#endif // __TZNAMES_IMPL_H__
//eof
//
