//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once

// Minimum amount of memory on the stack required by a visitor call.
// Use this value to control when to stop the visitor recursion before a SOE occurs.
#define PNODEVISIRORSIZE 256

template <class Context>
struct VisitorPolicyBase
{
    typedef Context Context;

protected:
    inline bool Preorder(ParseNode *pnode, Context context) { return true; }
    inline void Inorder(ParseNode *pnode, Context context) { }
    inline void Midorder(ParseNode *pnode, Context context) { }
    inline void Postorder(ParseNode *pnode, Context context) { }
    inline void InList(ParseNode *pnode, Context context) { }

    // This will be useful when you want the reference of your current node.
    inline void PassReference(ParseNode **ppnode, Context context) { }
};

template <class Visitor, class VisitorPolicy>
struct VisitorWalkerPolicy : public VisitorPolicy
{
public:
    typedef ParseNode *ResultType;
    typedef struct WalkerContext
    {
        typename VisitorPolicy::Context visitorContext;
        Visitor *visitor;
        WalkerContext(typename VisitorPolicy::Context context, Visitor *visitor): visitorContext(context), visitor(visitor) { }
    } *Context;
    inline ParseNode *DefaultResult() { return NULL; }
    inline bool ContinueWalk(ParseNode *result) { return true; }
    inline ParseNode *WalkNode(ParseNode *pnode, Context context) { Inorder(pnode, context->visitorContext); return pnode; }
    inline ParseNode *WalkListNode(ParseNode *pnode, Context context) { InList(pnode, context->visitorContext); return NULL; }
    inline ParseNode *WalkFirstChild(ParseNode *pnode, Context context) { context->visitor->VisitNode(pnode, context); return pnode; }
    inline ParseNode *WalkSecondChild(ParseNode *pnode, Context context) { context->visitor->VisitNode(pnode, context); return pnode; }
    inline ParseNode *WalkNthChild(ParseNode *pparentnode, ParseNode *pnode, Context context) { Midorder(pparentnode, context->visitorContext); context->visitor->VisitNode(pnode, context); return pnode; }
    inline void WalkReference(ParseNode **ppnode, Context context) { context->visitor->PassReferenceNode(ppnode, context); }
};

template <class VisitorPolicy>
class ParseNodeVisitor : public ParseNodeWalker<VisitorWalkerPolicy<ParseNodeVisitor<VisitorPolicy>, VisitorPolicy> >
{
    typedef VisitorWalkerPolicy<ParseNodeVisitor<VisitorPolicy>, VisitorPolicy> WalkerPolicy;
    typedef typename WalkerPolicy::WalkerContext WalkerContext;

public:
    typedef typename VisitorPolicy::Context VisitorContext;

    void Visit(ParseNode *pnode, VisitorContext context = VisitorContext())
    {
        WalkerContext walkerContext(context, this);
        VisitNode(pnode, &walkerContext);
    }

    void VisitNode(ParseNode *pnode, Context context)
    {
        if (!ThreadContext::IsCurrentStackAvailable(PNODEVISIRORSIZE))
            return;

        if (!pnode) return;

        if (!Preorder(pnode, context->visitorContext))
            return;

        Walk(pnode, context);

        Postorder(pnode, context->visitorContext);
    }

    void PassReferenceNode(ParseNode **ppnode, Context context)
    {
        PassReference(ppnode, context->visitorContext);
    }
};

