package parser

import (
	"github.com/marzeq/qk/attributes"
	"github.com/marzeq/qk/tokeniser"
)

// BlockResult returns the expression yielded when the block reaches its end.
func BlockResult(block *BlockNode) (ExpressionNode, bool) {
	if block == nil || !block.Expression || len(block.Body) == 0 {
		return nil, false
	}
	expr, ok := block.Body[len(block.Body)-1].(ExpressionNode)
	if !ok {
		return nil, false
	}
	if nested, ok := expr.(*BlockNode); ok && !nested.Expression {
		return nil, false
	}
	if conditional, ok := expr.(*IfNode); ok && !conditional.Expression {
		return nil, false
	}
	if match, ok := expr.(*MatchNode); ok && !match.Expression {
		return nil, false
	}
	return expr, true
}

// NodeFallsThrough reports whether execution can continue past a node.
func NodeFallsThrough(node Node) bool {
	switch n := node.(type) {
	case *ControlKeywordNode:
		return n.Keyword != tokeniser.KeywordReturn &&
			n.Keyword != tokeniser.KeywordBreak &&
			n.Keyword != tokeniser.KeywordContinue
	case *BlockNode:
		for _, child := range n.Body {
			if !NodeFallsThrough(child) {
				return false
			}
		}
		return true
	case *IfNode:
		if n.ElseBranch == nil || NodeFallsThrough(n.IfBranch.Node) {
			return true
		}
		for _, branch := range n.ElseIfBranches {
			if NodeFallsThrough(branch.Node) {
				return true
			}
		}
		return NodeFallsThrough(n.ElseBranch)
	case *MatchNode:
		for _, arm := range n.Arms {
			if NodeFallsThrough(arm.Body) {
				return true
			}
		}
		return false
	case *FunctionCallNode:
		return n.Symbol == nil || n.Symbol.Attributes.Get(attributes.AttributeTypeNoReturn) == nil
	default:
		return true
	}
}