// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.

// The Cangjie API is in Beta. For details on its capabilities and limitations, please refer to the README file.


#ifndef CANGJIE_DECOMPRESS_H
#define CANGJIE_DECOMPRESS_H

#include <string>
#include <vector>
#include <tuple>

namespace Cangjie {
constexpr size_t NPOS = static_cast<size_t>(-1);

template<typename T>
class DeCompression {
public:
    /**
     * @brief The constructor of class DeCompression.
     *
     * @param mangled The name to decompress.
     * @return DeCompression The instance of DeCompression.
     */
    explicit DeCompression(const T& mangled) : isRecord(false)
    {
        mangledName = mangled;
    }

    /**
     * @brief Main entry of mangler decompression.
     *
     * @param isType Whether the demangled name is type.
     * @return T The demangled name after decompression.
     */
    T CJMangledDeCompression(bool isType = false);

    /**
     * @brief Get the index at the end of Class type.
     *
     * @param mangled The name needs to decompress.
     * @param cnt Record the number of new elements added to the treeIdMap.
     * @param idx The start index of the demangled name.
     * @return size_t The end index of the demangled name.
     */
    size_t ForwardClassType(T& mangled, size_t& cnt, size_t idx);

    /**
     * @brief Get the index at the end of Generic types. e.g. I<type>+E
     *
     * @param mangled The name needs to decompress.
     * @param cnt Record the number of new elements added to the treeIdMap.
     * @param idx The start index of the demangled name.
     * @return size_t The end index of the demangled name.
     */
    size_t ForwardGenericTypes(T& mangled, size_t& cnt, size_t idx);

    /**
     * @brief Get the index at the end of Function type.
     *
     * @param mangled The name needs to decompress.
     * @param cnt Record the number of new elements added to the treeIdMap.
     * @param idx The start index of the demangled name.
     * @return size_t The end index of the demangled name.
     */
    size_t ForwardFunctionType(T& mangled, size_t& cnt, size_t idx);

    /**
     * @brief Get the index at the end of Tuple type.
     *
     * @param mangled The name needs to decompress.
     * @param cnt Record the number of new elements added to the treeIdMap.
     * @param idx The start index of the demangled name.
     * @return size_t The end index of the demangled name.
     */
    size_t ForwardTupleType(T& mangled, size_t& cnt, size_t idx);

    /**
     * @brief Get the index at the end of CPointer type.
     *
     * @param mangled The name needs to decompress.
     * @param cnt Record the number of new elements added to the treeIdMap.
     * @param idx The start index of the demangled name.
     * @return size_t The end index of the demangled name.
     */
    size_t ForwardCPointer(T& mangled, size_t& cnt, size_t idx);

    /**
     * @brief Get the index at the end of Array type.
     *
     * @param mangled The name needs to decompress.
     * @param cnt Record the number of new elements added to the treeIdMap.
     * @param idx The start index of the demangled name.
     * @return size_t The end index of the demangled name.
     */
    size_t ForwardArrayType(T& mangled, size_t& cnt, size_t idx);

    /**
     * @brief Get the index at the end of Generic type. e.g. G<type>
     *
     * @param mangled The name needs to decompress.
     * @param cnt Record the number of new elements added to the treeIdMap.
     * @param idx The start index of the demangled name.
     * @return size_t The end index of the demangled name.
     */
    size_t ForwardGenericType(T& mangled, size_t& cnt, size_t idx);

    /**
     * @brief Get the index at the end of package name.
     *
     * @param mangled The name needs to decompress.
     * @param cnt Record the number of new elements added to the treeIdMap.
     * @param idx The start index of the demangled name.
     * @return size_t The end index of the demangled name.
     */
    size_t ForwardPackageName(T& mangled, size_t& cnt, size_t idx);

    /**
     * @brief Get the index at the end of the type.
     *
     * @param mangled The name needs to decompress.
     * @param cnt Record the number of new elements added to the treeIdMap.
     * @param idx The start index of the demangled name.
     * @return size_t The end index of the demangled name.
     */
    size_t ForwardType(T& mangled, size_t& cnt, size_t idx = 0);

    /**
     * @brief Get the index at the end of the types.
     *
     * @param mangled The name needs to decompress.
     * @param cnt Record the number of new elements added to the treeIdMap.
     * @param idx The start index of the demangled name.
     * @return size_t The end index of the demangled name.
     */
    size_t ForwardTypes(T& mangled, size_t& cnt, size_t startId = 0);

    /**
     * @brief Get the index at the end of the number.
     *
     * @param mangled The name needs to decompress.
     * @param idx The start index of the demangled name.
     * @return size_t The end index of the demangled name.
     */
    size_t ForwardNumber(T& mangled, size_t idx = 0);

    /**
     * @brief Get the index at the end of the name.
     *
     * @param mangled The name needs to decompress.
     * @param idx The start index of the demangled name.
     * @return size_t The end index of the demangled name.
     */
    size_t ForwardName(T& mangled, size_t idx = 0);

    /**
     * @brief Check whether the demangle name is compressed.
     *
     * @param mangled The name needs to decompress.
     * @return bool If yes, true is returned. Otherwise, false is returned.
     */
    bool IsCompressed(T& mangled);

    /**
     * @brief Check whether the demangle name is variable decl.
     *
     * @param mangled The name needs to decompress.
     * @return bool If yes, true is returned. Otherwise, false is returned.
     */
    bool IsVarDeclEncode(T& mangled);

    /**
     * @brief Check whether the demangle name is global name.
     *
     * @param mangled The name needs to decompress.
     * @return bool If yes, true is returned. Otherwise, false is returned.
     */
    bool IsGlobalEncode(T& mangled);

    /**
     * @brief Check whether the demangle name is default param function.
     *
     * @param mangled The name needs to decompress.
     * @return bool If yes, true is returned. Otherwise, false is returned.
     */
    bool IsDefaultParamFuncEncode(T& mangled);

    /**
     * @brief Check if the first and the second have the same prefix.
     *
     * @param first The first string.
     * @param second The second string.
     * @param idx The start index of the first and the second.
     * @return bool If yes, true is returned. Otherwise, false is returned.
     */
    bool IsSamePrefix(T& first, T second, size_t idx);

    /**
     * @brief Check if the treeIdMap vector contains duplicates.
     *
     * @param mangled The name needs to decompress.
     * @param pos The position which is a pair consisting of start index and length.
     * @return bool If yes, true is returned. Otherwise, false is returned.
     */
    bool HasDuplicates(T& mangled, std::tuple<size_t, size_t>& pos);

    /**
     * @brief Check if the treeIdMap vector contains duplicates.
     *
     * @param mangled The name needs to decompress.
     * @param mid The mid-th element in the treeIdMap vector.
     * @return bool If yes, true is returned. Otherwise, false is returned.
     */
    bool HasDuplicates(T& mangled, size_t mid);

    /**
     * @brief Update the compressed name.
     *
     * @param compressed The name needs to update.
     * @param idx The start index of the compressed name.
     * @return std::tuple<size_t, size_t> A pair consisting of the change count and the end index.
     */
    std::tuple<size_t, size_t> UpdateCompressedName(T& compressed, size_t idx);

    /**
     * @brief Update the compressed name.
     *
     * @param compressed The name needs to update.
     * @param sid The start index which the compressed name has been updated.
     * @param eid The end index which the compressed name has been updated.
     * @return size_t The real end index which the compressed name has been updated.
     */
    size_t UpdateCompressedName(T& compressed, size_t sid, size_t eid);

    /**
     * @brief Try to decompress extend path.
     *
     * @param mangled The compressed name will be updated.
     * @param count Record the number of new elements added to the treeIdMap.
     * @param idx The start index of the compressed name.
     * @param entityId The index of treeIdMap vector whose item needs to update the end index.
     * @param curMangled The origin compressed name.
     * @return size_t The end index which the compressed name has been updated.
     */
    size_t TryExtendPath(T& mangled, size_t& count, size_t idx, size_t entityId, T& curMangled);

    /**
     * @brief Try to decompress lambda path.
     *
     * @param mangled The compressed name will be updated.
     * @param count Record the number of new elements added to the treeIdMap.
     * @param idx The start index of the compressed name.
     * @param entityId The index of treeIdMap vector whose item needs to update the end index.
     * @param change Indicates whether the compressed name has been updated.
     * @return size_t The end index which the compressed name has been updated.
     */
    size_t TryLambdaPath(T& mangled, size_t& count, size_t idx, size_t entityId, size_t change);

    /**
     * @brief Try to decompress generic prefix path.
     *
     * @param mangled The compressed name will be updated.
     * @param count Record the number of new elements added to the treeIdMap.
     * @param curMangled The origin compressed name.
     * @param rParams A tuple consisting of the start index, entity id and the end index.
     * @return size_t The end index which the compressed name has been updated.
     */
    size_t TryGenericPrefixPath(T& mangled, size_t& count, T& curMangled, std::tuple<size_t, size_t, size_t> rParams);

    /**
     * @brief Try to decompress name prefix path.
     *
     * @param mangled The compressed name will be updated.
     * @param count Record the number of new elements added to the treeIdMap.
     * @param curMangled The origin compressed name.
     * @param rParams A tuple consisting of the start index, entity id and the change.
     * @return size_t The end index which the compressed name has been updated.
     */
    size_t TryNamePrefixPath(T& mangled, size_t& count, T& curMangled, std::tuple<size_t, size_t, size_t> rParams);

    /**
     * @brief Try to decompress path.
     *
     * @param mangled The compressed name will be updated.
     * @return size_t The end index which the compressed name has been updated.
     */
    size_t TryParsePath(T mangled);

    /**
     * @brief Generate variable decl decompressed name.
     */
    void SpanningVarDeclTree();

    /**
     * @brief Generate function decl decompressed name.
     */
    void SpanningFuncDeclTree();

    /**
     * @brief Pop elements of treeIdMap vector.
     *
     * @param from The start index of treeIdMap vector.
     * @param to The end index of treeIdMap vector.
     */
    void TreeIdMapPop(size_t& from, size_t to);

    /**
     * @brief Push elements of treeIdMap vector.
     *
     * @param mangled The compressed name.
     * @param pos The element needs to push.
     * @return bool If push success, true is returned. Otherwise, false is returned.
     */
    bool TreeIdMapPushBack(T& mangled, std::tuple<size_t, size_t>& pos);

    /**
     * @brief Erase elements of treeIdMap vector.
     *
     * @param mangled The compressed name.
     * @param cnt The treeIdMap size.
     * @param eid The entity id.
     * @param sid The start index of the compressed name(mangled).
     * @return T The compressed name(mangled) has been updated.
     */
    T TreeIdMapErase(T& mangled, size_t& cnt, size_t eid, size_t sid);

    /**
     * @brief Assign elements of treeIdMap vector.
     *
     * @param mangled The compressed name.
     * @param mangledCopy The origin compressed name.
     * @param mapId The index of treeIdMap vector whose item needs to update the end index.
     * @param cnt The treeIdMap size.
     * @param eleInfo A pair consisting of start index and end index.
     */
    void TreeIdMapAssign(T& mangled, T& mangledCopy, size_t mapId, size_t& cnt,
        std::tuple<size_t, size_t>& eleInfo);

private:
    size_t pid{};
    bool isRecord;
    T mangledName;
    size_t currentIndex{};
    T decompressed;
    std::vector<std::tuple<size_t, size_t>> treeIdMap;
};
}
#endif // CANGJIE_DECOMPRESS_H