/*
 * Copyright (c) 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 HC_GEN_AST_H
#define HC_GEN_AST_H

#include <list>
#include <memory>
#include <string>
#include <vector>

#include "token.h"
#include "types.h"

namespace OHOS {
namespace Hardware {
enum ObjectType {
    PARSEROP_UINT8 = 0x01,
    PARSEROP_UINT16,
    PARSEROP_UINT32,
    PARSEROP_UINT64,
    PARSEROP_STRING,
    PARSEROP_CONFNODE,
    PARSEROP_CONFTERM,
    PARSEROP_ARRAY,
    PARSEROP_NODEREF,
    PARSEROP_DELETE,
};

enum NodeRefType {
    NODE_NOREF = 0,
    NODE_COPY,
    NODE_REF,
    NODE_DELETE,
    NODE_TEMPLATE,
    NODE_INHERIT,
};

class AstObject {
public:
    friend class Ast;

    AstObject(const std::string &name, uint32_t type, uint64_t integerValue, const std::string &strValue,
        uint32_t lineno, const std::shared_ptr<std::string> &src);

    AstObject(const std::string &name, uint32_t type, uint64_t integerValue);

    AstObject(const std::string &name, uint32_t type, const std::string &strValue);

    AstObject(const std::string &name, uint32_t type, uint64_t integerValue, const Token &bindToken);

    AstObject(const std::string &name, uint32_t type, const std::string &strValue, const Token &bindToken);

    AstObject(const AstObject &obj);

    virtual ~AstObject();

    AstObject &operator=(const AstObject &obj);

    virtual bool AddChild(const std::shared_ptr<AstObject> &childObj);

    virtual bool AddPeer(std::shared_ptr<AstObject> peerObject);

    friend std::ostream &operator<<(std::ostream &stream, const AstObject &t);

    virtual bool Merge(std::shared_ptr<AstObject> &srcObj);

    virtual bool Copy(std::shared_ptr<AstObject> src, bool overwrite);

    virtual bool Move(std::shared_ptr<AstObject> src);

    void Remove();

    void Separate();

    std::shared_ptr<AstObject> Lookup(const std::string &name, uint32_t type = 0) const;

    bool IsElders(const std::shared_ptr<AstObject> &child) const;

    bool IsNumber() const;

    bool IsNode() const;

    bool IsTerm() const;

    bool IsArray() const;

    virtual std::string SourceInfo();

    void SetParent(AstObject *parent);

    void SetSize(uint32_t size);

    void SetSubSize(uint32_t size);

    void SetHash(uint32_t hash);

    uint32_t GetSize() const;

    uint32_t GetSubSize() const;

    uint32_t GetHash() const;

    std::shared_ptr<AstObject> Child();

    std::shared_ptr<AstObject> Next();

    virtual const std::string &Name();

    const std::string &StringValue() const;

    uint64_t IntegerValue() const;

    virtual uint32_t Type();

    uint8_t OpCode() const;

    void SetOpCode(uint8_t opcode);

    virtual bool HasDuplicateChild();

    std::shared_ptr<AstObject> Parent() const;

protected:
    uint32_t type_;
    std::string name_;
    AstObject *parent_;
    std::shared_ptr<AstObject> next_;
    std::shared_ptr<AstObject> child_;
    uint32_t lineno_;
    std::shared_ptr<std::string> src_;
    uint8_t opCode_;
    uint32_t size_;
    uint32_t subSize_;
    uint32_t hash_;
    uint64_t integerValue_;
    std::string stringValue_;

private:
    static uint32_t FitIntegerValueType(uint64_t value);
};

class ConfigNode : public AstObject {
public:
    ConfigNode(const ConfigNode &node);

    ConfigNode(const std::string &name, uint32_t nodeType, const std::string &refName);

    ConfigNode(Token &name, uint32_t nodeType, const std::string &refName);

    ~ConfigNode() override = default;

    ConfigNode &operator=(const ConfigNode &node);

    friend std::ostream &operator<<(std::ostream &stream, const ConfigNode &t);

    bool Merge(std::shared_ptr<AstObject> &srcObj) override;

    static ConfigNode *CastFrom(const std::shared_ptr<AstObject> &astObject);

    uint32_t GetNodeType() const;

    const std::string &GetRefPath() const;

    void SetNodeType(uint32_t nodeType);

    void SetRefPath(const std::string &ref);

    static const std::string &NodeTypeToStr(uint32_t type);

    bool HasDuplicateChild() override;

    bool InheritExpand(const std::shared_ptr<AstObject> &refObj);

    bool RefExpand(const std::shared_ptr<AstObject> &refObj);

    bool Copy(std::shared_ptr<AstObject> src, bool overwrite) override;

    bool Move(std::shared_ptr<AstObject> src) override;

    bool Compare(ConfigNode &other) const;

    bool IsBaseNode();

    uint32_t InheritIndex() const;

    uint32_t InheritCount() const;

    uint32_t TemplateSignNum() const;

    void SetTemplateSignNum(uint32_t sigNum);

    const std::list<AstObject *> &SubClasses() const;

private:
    bool NodeRefExpand(const std::shared_ptr<AstObject> &ref);

    bool NodeCopyExpand(const std::shared_ptr<AstObject> &ref);

    std::string refNodePath_;
    uint32_t nodeType_;
    uint32_t inheritIndex_;
    uint32_t inheritCount_;
    uint32_t templateSignNum_;
    std::list<AstObject *> subClasses_;
};

class ConfigTerm : public AstObject {
public:
    ConfigTerm(const ConfigTerm &term);

    ConfigTerm(const std::string &name, const std::shared_ptr<AstObject> &value);

    ConfigTerm(Token &name, const std::shared_ptr<AstObject> &value);

    ~ConfigTerm() override = default;

    ConfigTerm &operator=(const ConfigTerm &term);

    static ConfigTerm *CastFrom(const std::shared_ptr<AstObject> &astObject);

    bool Merge(std::shared_ptr<AstObject> &srcObj) override;

    friend std::ostream &operator<<(std::ostream &stream, const ConfigTerm &t);

    bool RefExpand(const std::shared_ptr<AstObject> refObj);

    bool Copy(std::shared_ptr<AstObject> src, bool overwrite) override;

    bool Move(std::shared_ptr<AstObject> src) override;

    std::weak_ptr<AstObject> RefNode();

    uint32_t SigNum() const;

    void SetSigNum(uint32_t sigNum);

private:
    std::weak_ptr<AstObject> refNode_;
    uint32_t signNum_;
};

class ConfigArray : public AstObject {
public:
    ConfigArray();

    ConfigArray(const ConfigArray &array);

    explicit ConfigArray(const Token &bindToken);

    ~ConfigArray() override = default;

    ConfigArray &operator=(const ConfigArray &array);

    static ConfigArray *CastFrom(const std::shared_ptr<AstObject> &astObject);

    bool AddChild(const std::shared_ptr<AstObject> &childObj) override;

    bool Merge(std::shared_ptr<AstObject> &srcObj) override;

    bool Copy(std::shared_ptr<AstObject> src, bool overwrite) override;

    uint16_t ArraySize() const;

    uint16_t ArrayType() const;

private:
    uint32_t arrayType_;
    uint32_t arraySize_;
};

class AstObjectFactory {
public:
    static std::shared_ptr<AstObject> Build(std::shared_ptr<AstObject> object);
};

class Ast {
public:
    explicit Ast(std::shared_ptr<AstObject> astRoot) : astRoot_(std::move(astRoot)), redefineChecked_(false) {}

    ~Ast() = default;

    std::shared_ptr<AstObject> GetAstRoot();

    bool Merge(const std::list<std::shared_ptr<Ast>> &astList);

    bool Expand();

    std::shared_ptr<AstObject> Lookup(const std::shared_ptr<AstObject> &startObj, const std::string &path);

    template <typename T>
    static bool WalkForward(const std::shared_ptr<AstObject> &startObject, T callback)
    {
        std::shared_ptr<AstObject> forwardWalkObj = startObject;
        int32_t walkDepth = 0;
        bool preVisited = false;

        while (forwardWalkObj != nullptr) {
            if (!preVisited) {
                int32_t ret = static_cast<int32_t>(callback(forwardWalkObj, walkDepth));
                if (ret && ret != EASTWALKBREAK) {
                    return false;
                } else if (ret != EASTWALKBREAK && forwardWalkObj->child_ != nullptr) {
                    /* when callback return EASTWALKBREAK, not walk current's child */
                    walkDepth++;
                    forwardWalkObj = forwardWalkObj->child_;
                    continue;
                }
            }
            if (forwardWalkObj == startObject) {
                break;
            }

            if (forwardWalkObj->next_ != nullptr) {
                forwardWalkObj = forwardWalkObj->next_;
                preVisited = false;
            } else {
                forwardWalkObj = forwardWalkObj->Parent();
                preVisited = true;
                walkDepth--;
            }
        }

        return true;
    }

    template <typename T>
    static bool WalkBackward(const std::shared_ptr<AstObject> &startObject, T callback)
    {
        std::shared_ptr<AstObject> backWalkObj = startObject;
        std::shared_ptr<AstObject> next = nullptr;
        std::shared_ptr<AstObject> parent = nullptr;
        int32_t walkDepth = 0;
        bool preVisited = false;

        while (backWalkObj != nullptr) {
            if (backWalkObj->child_ == nullptr || preVisited) {
                next = backWalkObj->next_;
                parent = backWalkObj->Parent();
                /* can safe delete current in callback */
                if (callback(backWalkObj, walkDepth) != NOERR) {
                    return false;
                }
            } else {
                if (backWalkObj->child_) {
                    walkDepth++;
                    backWalkObj = backWalkObj->child_;
                    continue;
                }
            }
            if (backWalkObj == startObject) {
                break;
            }

            if (next != nullptr) {
                backWalkObj = next;
                preVisited = false;
            } else {
                backWalkObj = parent;
                preVisited = true;
                walkDepth--;
            }
        }

        return true;
    }

    template <typename T1, typename T2>
    static bool WalkRound(const std::shared_ptr<AstObject> &startObject, T1 forwardCallback, T2 backwardCallback)
    {
        std::shared_ptr<AstObject> roundWalkObj = startObject;
        int32_t walkDepth = 0;
        bool preVisited = false;

        while (roundWalkObj != nullptr) {
            if (preVisited) {
                if (backwardCallback(roundWalkObj, walkDepth) != NOERR) {
                    return false;
                }
            } else {
                auto ret = forwardCallback(roundWalkObj, walkDepth);
                /* when callback return EASTWALKBREAK, not walk current's child */
                if (ret && ret != EASTWALKBREAK) {
                    return false;
                } else if (!ret && roundWalkObj->child_ != nullptr) {
                    walkDepth++;
                    roundWalkObj = roundWalkObj->child_;
                    continue;
                }
            }
            if (roundWalkObj == startObject) {
                break;
            }

            if (roundWalkObj->next_) {
                roundWalkObj = roundWalkObj->next_;
                preVisited = false;
            } else {
                roundWalkObj = roundWalkObj->Parent();
                preVisited = true;
                walkDepth--;
            }
        }

        return true;
    }

    template <typename T>
    bool WalkForward(T callback)
    {
        return WalkForward(astRoot_, callback);
    }

    template <typename T>
    bool WalkBackward(T callback)
    {
        return WalkBackward(astRoot_, callback);
    }

    template <typename T1, typename T2>
    bool WalkRound(T1 forwardCallback, T2 backwardCallback)
    {
        return WalkRound(astRoot_, forwardCallback, backwardCallback);
    }

    void Dump(const std::string &prefix = std::string());

private:
    bool RedefineCheck();

    bool NodeExpand();

    bool NodeExpandRef();

    bool NodeExpandDelete();

    bool NodeExpandTermRef();

    bool InheritExpand();

    static std::list<std::string> SplitNodePath(const std::string &path, char separator);

    std::shared_ptr<AstObject> astRoot_;
    bool redefineChecked_;
};

std::ostream &operator<<(std::ostream &stream, const AstObject &t);
std::ostream &operator<<(std::ostream &stream, const ConfigNode &t);
std::ostream &operator<<(std::ostream &stream, const ConfigTerm &t);
} // namespace Hardware
} // namespace OHOS

#endif // HC_GEN_AST_H