/**
 * 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_IR_EXPRESSION_ASSIGNMENT_EXPRESSION_H
#define ES2PANDA_IR_EXPRESSION_ASSIGNMENT_EXPRESSION_H

#include "ir/expression.h"
#include "generated/tokenType.h"
namespace ark::es2panda::checker {
class ETSAnalyzer;
}  // namespace ark::es2panda::checker
namespace ark::es2panda::ir {
class AssignmentExpression : public Expression {
private:
    struct Tag {};

public:
    AssignmentExpression() = delete;
    ~AssignmentExpression() override = default;

    AssignmentExpression &operator=(const AssignmentExpression &) = delete;
    NO_MOVE_SEMANTIC(AssignmentExpression);

    explicit AssignmentExpression(Expression *const left, Expression *const right,
                                  lexer::TokenType const assignmentOperator)
        : AssignmentExpression(AstNodeType::ASSIGNMENT_EXPRESSION, left, right, assignmentOperator)
    {
    }

    explicit AssignmentExpression(AstNodeType const type, Expression *const left, Expression *const right,
                                  lexer::TokenType const assignmentOperator)
        : Expression(type), left_(left), right_(right), operator_(assignmentOperator)
    {
    }

    explicit AssignmentExpression(Tag tag, AssignmentExpression const &other, Expression *left, Expression *right);

    // NOTE (vivienvoros): these friend relationships can be removed once there are getters for private fields
    friend class checker::ETSAnalyzer;

    [[nodiscard]] const Expression *Left() const noexcept
    {
        return left_;
    }

    [[nodiscard]] Expression *Left() noexcept
    {
        return left_;
    }

    [[nodiscard]] Expression *Right() noexcept
    {
        return right_;
    }

    [[nodiscard]] const Expression *Right() const noexcept
    {
        return right_;
    }

    void SetRight(Expression *const expr) noexcept
    {
        right_ = expr;

        if (right_ != nullptr) {
            right_->SetParent(this);
        }
    }

    void SetLeft(Expression *const expr) noexcept
    {
        left_ = expr;

        if (left_ != nullptr) {
            left_->SetParent(this);
        }
    }

    [[nodiscard]] const Expression *Result() const noexcept
    {
        return result_;
    }

    [[nodiscard]] Expression *Result() noexcept
    {
        return result_;
    }

    [[nodiscard]] lexer::TokenType OperatorType() const noexcept
    {
        return operator_;
    }

    lexer::TokenType SetOperatorType(lexer::TokenType tokenType) noexcept
    {
        return operator_ = tokenType;
    }

    void SetResult(Expression *expr) noexcept
    {
        result_ = expr;
        if (result_ != nullptr) {
            result_->SetParent(this);
        }
    }

    [[nodiscard]] bool IsLogicalExtended() const noexcept
    {
        return operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND ||
               operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR;
    }

    [[nodiscard]] varbinder::Variable *Target() noexcept
    {
        return target_;
    }

    [[nodiscard]] varbinder::Variable *Target() const noexcept
    {
        return target_;
    }

    void SetIgnoreConstAssign()
    {
        ignoreConstAssign_ = true;
    }

    [[nodiscard]] bool IsIgnoreConstAssign() const
    {
        return ignoreConstAssign_;
    }

    [[nodiscard]] AssignmentExpression *Clone(ArenaAllocator *allocator, AstNode *parent) override;

    [[nodiscard]] bool ConvertibleToAssignmentPatternLeft(bool mustBePattern);
    [[nodiscard]] bool ConvertibleToAssignmentPatternRight();
    [[nodiscard]] bool ConvertibleToAssignmentPattern(bool mustBePattern = true);

    void TransformChildren(const NodeTransformer &cb, std::string_view transformationName) override;
    void Iterate(const NodeTraverser &cb) const override;
    void Dump(ir::AstDumper *dumper) const override;
    void Dump(ir::SrcDumper *dumper) const override;
    void Compile(compiler::PandaGen *pg) const override;
    void Compile(compiler::ETSGen *etsg) const override;
    void CompilePattern(compiler::PandaGen *pg) const;
    checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override;
    checker::VerifiedType Check([[maybe_unused]] checker::ETSChecker *checker) override;

    void Accept(ASTVisitorT *v) override
    {
        v->Accept(this);
    }

    void CleanUp() override
    {
        AstNode::CleanUp();
        target_ = nullptr;
        operationType_ = nullptr;
    }

    void CleanCheckInformation() override;

protected:
    AssignmentExpression(AssignmentExpression const &other) : Expression(static_cast<Expression const &>(other))
    {
        operator_ = other.operator_;
        target_ = other.target_;
        operationType_ = other.operationType_;
        ignoreConstAssign_ = other.ignoreConstAssign_;
    }

private:
    EPtr<Expression> left_ = nullptr;
    EPtr<Expression> right_ = nullptr;
    EPtr<Expression> result_ = nullptr;
    EPtr<varbinder::Variable> target_ {};
    EPtr<checker::Type> operationType_ {};
    lexer::TokenType operator_;
    bool ignoreConstAssign_ = false;
};
}  // namespace ark::es2panda::ir

#endif