// Copyright 2020 CUE Authors
//
// 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.

package adt

import (
	"cuelang.org/go/cue/ast"
	"cuelang.org/go/cue/token"
)

func Resolve(ctx *OpContext, c Conjunct) *Vertex {
	env := c.Env
	// TODO: also allow resolution in parent scopes. The following will set up
	// the environments. But the compiler also needs to resolve accordingly.
	//
	// // Set up environments for parent scopes, if any.
	// root := env
	// for p := scope; p != nil; p = p.Parent {
	// 	root.Up = &Environment{Vertex: p.Parent}
	// 	root = root.Up
	// }

	var v Value

	expr := c.Elem() // TODO: why is this not Expr?
	switch x := expr.(type) {
	case Value:
		v = x

	case Resolver:
		r, err := ctx.resolveState(c, x, Flags{
			status:    finalized,
			condition: allKnown,
			mode:      attemptOnly,
		})
		if err != nil {
			v = err
			break
		}
		// r.Finalize(ctx) // TODO: Finalize here?
		return r

	case Evaluator:
		// TODO: have a way to evaluate, but not strip down to the value.
		v, _ = ctx.Evaluate(env, expr.(Expr))

	default:
		// Unknown type.
		v = ctx.NewErrf(
			"could not evaluate expression %s of type %T", c.Elem(), c)
	}

	return ToVertex(v)
}

// A Node is any abstract data type representing a value or expression.
type Node interface {
	Source() ast.Node
	node() // enforce internal.
}

// A Decl represents all valid StructLit elements.
type Decl interface {
	Node
	declNode()
}

// An Elem represents all value ListLit elements.
//
// All Elem values can be used as a Decl.
type Elem interface {
	Decl
	elemNode()
}

// An Expr corresponds to an ast.Expr.
//
// All Expr values can be used as an Elem or Decl.
type Expr interface {
	Elem
	expr()
}

// A BaseValue is any Value or a *Marker. It indicates the type of a Vertex.
type BaseValue interface {
	Kind() Kind
}

// A Value represents a node in the evaluated data graph.
//
// All Values values can also be used as a Expr.
type Value interface {
	Expr
	Concreteness() Concreteness
	Kind() Kind
}

// An Evaluator provides a method to convert to a value.
type Evaluator interface {
	Node

	// evaluate evaluates the underlying expression. If the expression
	// is incomplete, it may record the error in ctx and return nil.
	evaluate(ctx *OpContext, state Flags) Value
}

// A Resolver represents a reference somewhere else within a tree that resolves
// a value.
type Resolver interface {
	Node
	resolve(ctx *OpContext, state Flags) *Vertex
}

type YieldFunc func(env *Environment)

// A Yielder represents 0 or more labeled values of structs or lists.
type Yielder interface {
	Node
	yield(s *compState)
}

// A Validator validates a Value. All Validators are Values.
type Validator interface {
	Value
	validate(c *OpContext, v Value) *Bottom
}

// Pos returns the file position of n, or token.NoPos if it is unknown.
func Pos(n Node) token.Pos {
	src := n.Source()
	if src == nil {
		return token.NoPos
	}
	return src.Pos()
}

// Value

func (x *Vertex) Concreteness() Concreteness {
	// Depends on concreteness of value.
	switch v := x.BaseValue.(type) {
	case nil:
		return Concrete // Should be indetermined.

	case Value:
		return v.Concreteness()

	default: //  *StructMarker, *ListMarker:
		return Concrete
	}
}

func (x *NodeLink) Concreteness() Concreteness { return Concrete }

func (*Conjunction) Concreteness() Concreteness { return Constraint }
func (*Disjunction) Concreteness() Concreteness { return Constraint }
func (*BoundValue) Concreteness() Concreteness  { return Constraint }

func (*Builtin) Concreteness() Concreteness          { return Concrete }
func (*BuiltinValidator) Concreteness() Concreteness { return Constraint }

// Value and Expr

func (*Bottom) Concreteness() Concreteness    { return BottomLevel }
func (*Null) Concreteness() Concreteness      { return Concrete }
func (*Bool) Concreteness() Concreteness      { return Concrete }
func (*Num) Concreteness() Concreteness       { return Concrete }
func (*String) Concreteness() Concreteness    { return Concrete }
func (*Bytes) Concreteness() Concreteness     { return Concrete }
func (*Top) Concreteness() Concreteness       { return Any }
func (*BasicType) Concreteness() Concreteness { return Type }

// Expr

func (*StructLit) expr()       {}
func (*ListLit) expr()         {}
func (*DisjunctionExpr) expr() {}

// TODO: also allow?
//       a: b: if cond {}
//
// It is unclear here, though, whether field `a` should be added
// unconditionally.
// func (*Comprehension) expr() {}

// Expr and Value

func (*Bottom) expr()           {}
func (*Null) expr()             {}
func (*Bool) expr()             {}
func (*Num) expr()              {}
func (*String) expr()           {}
func (*Bytes) expr()            {}
func (*Top) expr()              {}
func (*BasicType) expr()        {}
func (*Vertex) expr()           {}
func (*ListMarker) expr()       {}
func (*StructMarker) expr()     {}
func (*Conjunction) expr()      {}
func (*Disjunction) expr()      {}
func (*BoundValue) expr()       {}
func (*BuiltinValidator) expr() {}
func (*Builtin) expr()          {}

// Expr and Resolver

func (*NodeLink) expr()         {}
func (*FieldReference) expr()   {}
func (*ValueReference) expr()   {}
func (*LabelReference) expr()   {}
func (*DynamicReference) expr() {}
func (*ImportReference) expr()  {}
func (*LetReference) expr()     {}

// Expr and Evaluator

func (*BoundExpr) expr()     {}
func (*SelectorExpr) expr()  {}
func (*IndexExpr) expr()     {}
func (*SliceExpr) expr()     {}
func (*Interpolation) expr() {}
func (*UnaryExpr) expr()     {}
func (*BinaryExpr) expr()    {}
func (*OpenExpr) expr()      {}
func (*CallExpr) expr()      {}

// Decl and Expr (so allow attaching original source in Conjunct)

func (*Field) declNode()                {}
func (x *Field) expr() Expr             { return x.Value }
func (*LetField) declNode()             {}
func (x *LetField) expr() Expr          { return x.Value }
func (*BulkOptionalField) declNode()    {}
func (x *BulkOptionalField) expr() Expr { return x.Value }
func (*DynamicField) declNode()         {}
func (x *DynamicField) expr() Expr      { return x.Value }

// Decl, Elem, and Expr (so allow attaching original source in Conjunct)

func (*Ellipsis) elemNode() {}
func (*Ellipsis) declNode() {}
func (x *Ellipsis) expr() Expr {
	if x.Value == nil {
		return top
	}
	return x.Value
}
func (*ConjunctGroup) declNode() {}
func (*ConjunctGroup) elemNode() {}
func (*ConjunctGroup) expr()     {}

var top = &Top{}

// Decl and Yielder

func (*LetClause) declNode() {}

// Decl and Elem

func (*StructLit) declNode()        {}
func (*StructLit) elemNode()        {}
func (*ListLit) declNode()          {}
func (*ListLit) elemNode()          {}
func (*Bottom) declNode()           {}
func (*Bottom) elemNode()           {}
func (*Null) declNode()             {}
func (*Null) elemNode()             {}
func (*Bool) declNode()             {}
func (*Bool) elemNode()             {}
func (*Num) declNode()              {}
func (*Num) elemNode()              {}
func (*String) declNode()           {}
func (*String) elemNode()           {}
func (*Bytes) declNode()            {}
func (*Bytes) elemNode()            {}
func (*Top) declNode()              {}
func (*Top) elemNode()              {}
func (*BasicType) declNode()        {}
func (*BasicType) elemNode()        {}
func (*BoundExpr) declNode()        {}
func (*BoundExpr) elemNode()        {}
func (*Vertex) declNode()           {}
func (*Vertex) elemNode()           {}
func (*ListMarker) declNode()       {}
func (*ListMarker) elemNode()       {}
func (*StructMarker) declNode()     {}
func (*StructMarker) elemNode()     {}
func (*Conjunction) declNode()      {}
func (*Conjunction) elemNode()      {}
func (*Disjunction) declNode()      {}
func (*Disjunction) elemNode()      {}
func (*BoundValue) declNode()       {}
func (*BoundValue) elemNode()       {}
func (*BuiltinValidator) declNode() {}
func (*BuiltinValidator) elemNode() {}
func (*NodeLink) declNode()         {}
func (*NodeLink) elemNode()         {}
func (*FieldReference) declNode()   {}
func (*FieldReference) elemNode()   {}
func (*ValueReference) declNode()   {}
func (*ValueReference) elemNode()   {}
func (*LabelReference) declNode()   {}
func (*LabelReference) elemNode()   {}
func (*DynamicReference) declNode() {}
func (*DynamicReference) elemNode() {}
func (*ImportReference) declNode()  {}
func (*ImportReference) elemNode()  {}
func (*LetReference) declNode()     {}
func (*LetReference) elemNode()     {}
func (*SelectorExpr) declNode()     {}
func (*SelectorExpr) elemNode()     {}
func (*IndexExpr) declNode()        {}
func (*IndexExpr) elemNode()        {}
func (*SliceExpr) declNode()        {}
func (*SliceExpr) elemNode()        {}
func (*Interpolation) declNode()    {}
func (*Interpolation) elemNode()    {}
func (*UnaryExpr) declNode()        {}
func (*UnaryExpr) elemNode()        {}
func (*BinaryExpr) declNode()       {}
func (*BinaryExpr) elemNode()       {}
func (*OpenExpr) declNode()         {}
func (*OpenExpr) elemNode()         {}
func (*CallExpr) declNode()         {}
func (*CallExpr) elemNode()         {}
func (*Builtin) declNode()          {}
func (*Builtin) elemNode()          {}
func (*DisjunctionExpr) declNode()  {}
func (*DisjunctionExpr) elemNode()  {}

// Decl, Elem, and Yielder

func (*Comprehension) declNode() {}
func (*Comprehension) elemNode() {}

// Node

func (*Vertex) node()            {}
func (*Conjunction) node()       {}
func (*OpenExpr) node()          {}
func (*ConjunctGroup) node()     {}
func (*Disjunction) node()       {}
func (*BoundValue) node()        {}
func (*Builtin) node()           {}
func (*BuiltinValidator) node()  {}
func (*Bottom) node()            {}
func (*Null) node()              {}
func (*Bool) node()              {}
func (*Num) node()               {}
func (*String) node()            {}
func (*Bytes) node()             {}
func (*Top) node()               {}
func (*BasicType) node()         {}
func (*StructLit) node()         {}
func (*ListLit) node()           {}
func (*BoundExpr) node()         {}
func (*NodeLink) node()          {}
func (*FieldReference) node()    {}
func (*ValueReference) node()    {}
func (*LabelReference) node()    {}
func (*DynamicReference) node()  {}
func (*ImportReference) node()   {}
func (*LetReference) node()      {}
func (*SelectorExpr) node()      {}
func (*IndexExpr) node()         {}
func (*SliceExpr) node()         {}
func (*Interpolation) node()     {}
func (*UnaryExpr) node()         {}
func (*BinaryExpr) node()        {}
func (*CallExpr) node()          {}
func (*DisjunctionExpr) node()   {}
func (*Field) node()             {}
func (*LetField) node()          {}
func (*BulkOptionalField) node() {}
func (*DynamicField) node()      {}
func (*Ellipsis) node()          {}
func (*Comprehension) node()     {}
func (*ForClause) node()         {}
func (*IfClause) node()          {}
func (*LetClause) node()         {}
