/**
 * Copyright (c) 2021-2026 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ES2PANDA_JSDOC_HELPER_H
#define ES2PANDA_JSDOC_HELPER_H

#include "util/ustring.h"
#include "lexer/token/letters.h"

namespace ark::es2panda::ir {
class AstNode;
}  // namespace ark::es2panda::ir
namespace ark::es2panda::parser {
class Program;

using UStringView = util::StringView;
class JsdocHelper {
public:
    explicit JsdocHelper(const ir::AstNode *inputNode)
    {
        InitNode(inputNode);
    }

    NO_COPY_SEMANTIC(JsdocHelper);
    DEFAULT_MOVE_SEMANTIC(JsdocHelper);

    ~JsdocHelper() = default;

    util::StringView GetJsdocBackward();
    util::StringView GetLicenseStringFromStart();

    const ir::AstNode *Node()
    {
        return node_;
    }

    util::StringView::Iterator &Iterator()
    {
        return iter_;
    }

    util::StringView SourceView(size_t begin, size_t end) const
    {
        return sourceCode_.Substr(begin, end);
    }

    void BackwardAndSkipSpace(size_t offset)
    {
        Backward(offset);
        SkipWhiteSpacesBackward();
    }

    void Backward(size_t offset)
    {
        const auto index = Iterator().Index();
        Iterator().Backward(offset > index ? index : offset);
    }

    void Forward(size_t offset)
    {
        Iterator().Forward(offset);
    }

    char32_t PeekBackWard() const
    {
        return iter_.Peek();
    }

    void InitNode(const ir::AstNode *input);

private:
    bool SkipWhiteSpacesBackwardHelper(const char32_t &cp)
    {
        if (cp < lexer::LEX_ASCII_MAX_BITS) {
            return false;
        }

        size_t cpSize {};

        char32_t ch = Iterator().PeekCp(&cpSize);
        switch (ch) {
            case lexer::LEX_CHAR_LS:
            case lexer::LEX_CHAR_PS:
            case lexer::LEX_CHAR_NBSP:
            case lexer::LEX_CHAR_ZWNBSP:
            case lexer::LEX_CHAR_OGHAM:
            case lexer::LEX_CHAR_NARROW_NO_BREAK_SP:
            case lexer::LEX_CHAR_MATHEMATICAL_SP:
            case lexer::LEX_CHAR_IDEOGRAPHIC_SP:
                Backward(cpSize);
                return true;
            default:
                if (ch >= lexer::LEX_CHAR_ENQUAD && ch <= lexer::LEX_CHAR_ZERO_WIDTH_SP) {
                    Backward(cpSize);
                    return true;
                }
                return false;
        }
    }

    void SkipWhiteSpacesBackward()
    {
        bool skipContinue = true;
        while (skipContinue) {
            if (Iterator().Index() == 0) {
                break;
            }
            auto cp = Iterator().Peek();
            switch (cp) {
                case lexer::LEX_CHAR_CR:
                case lexer::LEX_CHAR_LF:
                case lexer::LEX_CHAR_VT:
                case lexer::LEX_CHAR_FF:
                case lexer::LEX_CHAR_SP:
                case lexer::LEX_CHAR_TAB:
                case lexer::LEX_CHAR_NEXT_LINE:
                    Backward(1U);
                    continue;
                default:
                    skipContinue = SkipWhiteSpacesBackwardHelper(cp);
            }
        }
    }

    void SkipCpBackward()
    {
        if (iter_.Index() == 0) {
            return;
        }
        Backward(1U);

        char32_t cu0 = static_cast<uint8_t>(iter_.Peek());
        if (cu0 < UStringView::Constants::UTF8_1BYTE_LIMIT) {
            return;
        }

        if ((cu0 & UStringView::Constants::UTF8_3BYTE_HEADER) == UStringView::Constants::UTF8_2BYTE_HEADER) {
            Backward(1U);
            return;
        }

        if ((cu0 & UStringView::Constants::UTF8_4BYTE_HEADER) == UStringView::Constants::UTF8_3BYTE_HEADER) {
            Backward(2U);
            return;
        }

        if (((cu0 & UStringView::Constants::UTF8_DECODE_4BYTE_MASK) == UStringView::Constants::UTF8_4BYTE_HEADER) &&
            (cu0 <= UStringView::Constants::UTF8_DECODE_4BYTE_LIMIT)) {
            Backward(3U);
        }
    }

    bool BackWardUntilJsdocStart();

    const ir::AstNode *root_ {};
    const parser::Program *program_ {};
    util::StringView sourceCode_ {};
    util::StringView::Iterator iter_ {nullptr};
    const ir::AstNode *node_ {};
};
}  // namespace ark::es2panda::parser

#endif