package yqlib

import (
	"fmt"

	logging "gopkg.in/op/go-logging.v1"
)

type DataTreeNavigator interface {
	// given the context and an expressionNode,
	// this will process the against the given expressionNode and return
	// a new context of matching candidates
	GetMatchingNodes(context Context, expressionNode *ExpressionNode) (Context, error)

	DeeplyAssign(context Context, path []interface{}, rhsNode *CandidateNode) error
}

type dataTreeNavigator struct {
}

func NewDataTreeNavigator() DataTreeNavigator {
	return &dataTreeNavigator{}
}

func (d *dataTreeNavigator) DeeplyAssign(context Context, path []interface{}, rhsCandidateNode *CandidateNode) error {

	assignmentOp := &Operation{OperationType: assignOpType, Preferences: assignPreferences{}}

	if rhsCandidateNode.Kind == MappingNode {
		log.Debug("DeeplyAssign: deeply merging object")
		// if the rhs is a map, we need to deeply merge it in.
		// otherwise we'll clobber any existing fields
		assignmentOp = &Operation{OperationType: multiplyAssignOpType, Preferences: multiplyPreferences{
			AppendArrays:  true,
			TraversePrefs: traversePreferences{DontFollowAlias: true},
			AssignPrefs:   assignPreferences{},
		}}
	}

	rhsOp := &Operation{OperationType: valueOpType, CandidateNode: rhsCandidateNode}

	assignmentOpNode := &ExpressionNode{
		Operation: assignmentOp,
		LHS:       createTraversalTree(path, traversePreferences{}, false),
		RHS:       &ExpressionNode{Operation: rhsOp},
	}

	_, err := d.GetMatchingNodes(context, assignmentOpNode)
	return err
}

func (d *dataTreeNavigator) GetMatchingNodes(context Context, expressionNode *ExpressionNode) (Context, error) {
	if expressionNode == nil {
		log.Debugf("getMatchingNodes - nothing to do")
		return context, nil
	}
	log.Debugf("Processing Op: %v", expressionNode.Operation.toString())
	if log.IsEnabledFor(logging.DEBUG) {
		for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
			log.Debug(NodeToString(el.Value.(*CandidateNode)))
		}
	}
	handler := expressionNode.Operation.OperationType.Handler
	if handler != nil {
		return handler(d, context, expressionNode)
	}
	return Context{}, fmt.Errorf("Unknown operator %v", expressionNode.Operation.OperationType)

}
