// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package node provides the structure for a tree of heterogeneous widget nodes.
//
// Most programmers should not need to import this package, only the top-level
// widget package. Only those that write custom widgets need to explicitly
// refer to the Node, Embed and related types.
//
// The Node interface is usually implemented by struct types that embed one of
// LeafEmbed, ShellEmbed or ContainerEmbed (all of which themselves embed an
// Embed), providing default implementations of all of Node's methods.
//
// The split between an outer wrapper (Node) interface type and an inner
// wrappee (Embed) struct type enables heterogeneous nodes, such as a buttons
// and labels, in a widget tree where every node contains common fields such as
// position, size and tree structure links (parent, siblings and children).
//
// In a traditional object-oriented type system, this might be represented by
// the Button and Label types both subclassing the Node type. Go does not have
// inheritance, so the outer / inner split is composed explicitly. For example,
// the concrete Button type is a struct type that embeds an XxxEmbed (such as
// LeafEmbed), and the NewButton function sets the inner Embed's Wrapper field
// to point back to the outer value.
//
// There are three layers here (Button embeds LeafEmbed embeds Embed) instead
// of two. The intermediate layer exists because there needs to be a place to
// provide default implementations of methods like Measure, but that place
// shouldn't be the inner-most type (Embed), otherwise it'd be too easy to
// write subtly incorrect code like:
//
//	for c := w.FirstChild; c != nil; c = c.NextSibling {
//		c.Measure(etc) // This should instead be c.Wrapper.Measure(etc).
//	}
//
// In any case, most programmers that want to construct a widget tree should
// not need to know this detail. It usually suffices to call functions such as
// widget.NewButton or widget.NewLabel, and then parent.Insert(button, nil).
//
// See the example/gallery program for some example code for a custom widget.
package node // import "golang.org/x/exp/shiny/widget/node"

import (
	"image"

	"golang.org/x/exp/shiny/gesture"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/exp/shiny/widget/theme"
	"golang.org/x/image/math/f64"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/mouse"
)

// EventHandled is whether or not an input event (a key, mouse, touch or
// gesture event) was handled by a widget. If it was not handled, the event is
// propagated along the widget tree.
type EventHandled bool

const (
	NotHandled = EventHandled(false)
	Handled    = EventHandled(true)
)

// NoHint means that there is no width or height hint in a Measure call.
const NoHint = -1

// Node is a node in the widget tree.
type Node interface {
	// Wrappee returns the inner (embedded) type that is wrapped by this type.
	Wrappee() *Embed

	// Insert adds a node c as a child of this node. If nextSibling is nil, c
	// will be inserted at the end of this node's children. Otherwise, c will
	// be inserted such that its next sibling is nextSibling.
	//
	// It will panic if c already has a parent or siblings.
	Insert(c, nextSibling Node)

	// Remove removes a node c that is a child of this node. Afterwards, c will
	// have no parent and no siblings.
	//
	// It will panic if c's parent is not this node.
	Remove(c Node)

	// Measure sets this node's Embed.MeasuredSize to its natural size, taking
	// its children into account.
	//
	// Some nodes' natural height might depend on their imposed width, such as
	// a text widget word-wrapping its contents. The caller may provide hints
	// that the parent can override the child's natural size in the width,
	// height or both directions. A negative value means that there is no hint.
	// For example, a container might lay out its children to all have the same
	// width, and could pass that width as the widthHint argument.
	Measure(t *theme.Theme, widthHint, heightHint int)

	// Layout lays out this node (and its children), setting the Embed.Rect
	// fields of each child. This node's Embed.Rect field should have
	// previously been set during the parent node's layout.
	Layout(t *theme.Theme)

	// Paint paints this node (and its children). Painting is split into two
	// passes: a base pass and an effects pass. The effects pass is often a
	// no-op, and the bulk of the work is typically done in the base pass.
	//
	// The base pass paints onto an *image.RGBA pixel buffer and ancestor nodes
	// may choose to re-use the result. For example, re-painting a text widget
	// after scrolling may copy cached buffers at different offsets, instead of
	// painting the text's glyphs onto a fresh buffer. Similarly, animating the
	// scale and opacity of an overlay can re-use the buffer from a previous
	// base pass.
	//
	// The effects pass paints that part of the widget that can not or should
	// not be cached. For example, the border of a text widget shouldn't move
	// on the screen when that text widget is scrolled. The effects pass does
	// not have a destination RGBA pixel buffer, and is limited to what a
	// screen.Drawer provides: affine-transformed textures and uniform fills.
	//
	// TODO: app-specific OpenGL, if available, should be part of the effects
	// pass. Is that exposed via the screen.Drawer or by another mechanism?
	//
	// The Paint method may create base pass RGBA pixel buffers, by calling
	// ctx.Screen.NewBuffer. Many implementations won't, and instead assume
	// that PaintBase is recursively triggered by an ancestor node such as a
	// widget.Sheet. If it does create those RGBA pixel buffers, it is also
	// responsible for calling PaintBase on this node (and its children). In
	// any case, the Paint method should then paint any effects. Many widgets
	// will neither create their own buffers nor have any effects, so their
	// Paint methods will simply be the default implementation: do nothing except
	// call Paint on its children. As mentioned above, the bulk of the work is
	// typically done in PaintBase.
	//
	// origin is the parent widget's origin with respect to the ctx.Src2Dst
	// transformation matrix; this node's Embed.Rect.Add(origin) will be its
	// position and size in pre-transformed coordinate space.
	Paint(ctx *PaintContext, origin image.Point) error

	// PaintBase paints the base pass of this node (and its children) onto an
	// RGBA pixel buffer.
	//
	// origin is the parent widget's origin with respect to the ctx.Dst image's
	// origin; this node's Embed.Rect.Add(origin) will be its position and size
	// in ctx.Dst's coordinate space.
	PaintBase(ctx *PaintBaseContext, origin image.Point) error

	// Mark adds the given marks to this node. It calls OnChildMarked on its
	// parent if new marks were added.
	Mark(m Marks)

	// OnChildMarked handles a child being given new marks. By default, marks
	// are propagated up the node tree towards the root. For example, a child
	// being marked for needing paint will cause the parent being marked for
	// needing paint.
	OnChildMarked(child Node, newMarks Marks)

	// OnLifecycleEvent propagates a lifecycle event to a node (and its
	// children).
	OnLifecycleEvent(e lifecycle.Event)

	// OnInputEvent handles a key, mouse, touch or gesture event.
	//
	// origin is the parent widget's origin with respect to the event origin;
	// this node's Embed.Rect.Add(origin) will be its position and size in
	// event coordinate space.
	OnInputEvent(e interface{}, origin image.Point) EventHandled

	// TODO: other OnXxxEvent methods?

}

// PaintContext is the context for the Node.Paint method.
type PaintContext struct {
	Theme   *theme.Theme
	Screen  screen.Screen
	Drawer  screen.Drawer
	Src2Dst f64.Aff3

	// TODO: add a clip rectangle?

	// TODO: add the DrawContext from the lifecycle event?
}

// PaintBaseContext is the context for the Node.PaintBase method.
type PaintBaseContext struct {
	Theme *theme.Theme
	Dst   *image.RGBA

	// TODO: add a clip rectangle? Or rely on the RGBA.SubImage method to pass
	// smaller Dst images?
}

// LeafEmbed is designed to be embedded in struct types for nodes with no
// children.
type LeafEmbed struct{ Embed }

func (m *LeafEmbed) Insert(c, nextSibling Node) {
	panic("node: Insert called for a leaf parent")
}

func (m *LeafEmbed) Remove(c Node) { m.remove(c) }

func (m *LeafEmbed) Measure(t *theme.Theme, widthHint, heightHint int) {
	m.MeasuredSize = image.Point{}
}

func (m *LeafEmbed) Layout(t *theme.Theme) {}

func (m *LeafEmbed) Paint(ctx *PaintContext, origin image.Point) error {
	m.Marks.UnmarkNeedsPaint()
	return nil
}

func (m *LeafEmbed) PaintBase(ctx *PaintBaseContext, origin image.Point) error {
	m.Marks.UnmarkNeedsPaintBase()
	return nil
}

func (m *LeafEmbed) OnChildMarked(child Node, newMarks Marks) {}

func (m *LeafEmbed) OnLifecycleEvent(e lifecycle.Event) {}

func (m *LeafEmbed) OnInputEvent(e interface{}, origin image.Point) EventHandled { return NotHandled }

// ShellEmbed is designed to be embedded in struct types for nodes with at most
// one child.
type ShellEmbed struct{ Embed }

func (m *ShellEmbed) Insert(c, nextSibling Node) {
	if m.FirstChild != nil {
		panic("node: Insert called for a shell parent that already has a child")
	}
	m.insert(c, nextSibling)
}

func (m *ShellEmbed) Remove(c Node) { m.remove(c) }

func (m *ShellEmbed) Measure(t *theme.Theme, widthHint, heightHint int) {
	if c := m.FirstChild; c != nil {
		c.Wrapper.Measure(t, widthHint, heightHint)
		m.MeasuredSize = c.MeasuredSize
	} else {
		m.MeasuredSize = image.Point{}
	}
}

func (m *ShellEmbed) Layout(t *theme.Theme) {
	if c := m.FirstChild; c != nil {
		c.Rect = m.Rect.Sub(m.Rect.Min)
		c.Wrapper.Layout(t)
	}
}

func (m *ShellEmbed) Paint(ctx *PaintContext, origin image.Point) error {
	m.Marks.UnmarkNeedsPaint()
	if c := m.FirstChild; c != nil {
		return c.Wrapper.Paint(ctx, origin.Add(m.Rect.Min))
	}
	return nil
}

func (m *ShellEmbed) PaintBase(ctx *PaintBaseContext, origin image.Point) error {
	m.Marks.UnmarkNeedsPaintBase()
	if c := m.FirstChild; c != nil {
		return c.Wrapper.PaintBase(ctx, origin.Add(m.Rect.Min))
	}
	return nil
}

func (m *ShellEmbed) OnChildMarked(child Node, newMarks Marks) {
	m.Mark(newMarks)
}

func (m *ShellEmbed) OnLifecycleEvent(e lifecycle.Event) {
	if c := m.FirstChild; c != nil {
		c.Wrapper.OnLifecycleEvent(e)
	}
}

func (m *ShellEmbed) OnInputEvent(e interface{}, origin image.Point) EventHandled {
	if c := m.FirstChild; c != nil {
		return c.Wrapper.OnInputEvent(e, origin.Add(m.Rect.Min))
	}
	return NotHandled
}

// ContainerEmbed is designed to be embedded in struct types for nodes with any
// number of children.
type ContainerEmbed struct{ Embed }

func (m *ContainerEmbed) Insert(c, nextSibling Node) { m.insert(c, nextSibling) }

func (m *ContainerEmbed) Remove(c Node) { m.remove(c) }

func (m *ContainerEmbed) Measure(t *theme.Theme, widthHint, heightHint int) {
	mSize := image.Point{}
	for c := m.FirstChild; c != nil; c = c.NextSibling {
		c.Wrapper.Measure(t, NoHint, NoHint)
		if mSize.X < c.MeasuredSize.X {
			mSize.X = c.MeasuredSize.X
		}
		if mSize.Y < c.MeasuredSize.Y {
			mSize.Y = c.MeasuredSize.Y
		}
	}
	m.MeasuredSize = mSize
}

func (m *ContainerEmbed) Layout(t *theme.Theme) {
	for c := m.FirstChild; c != nil; c = c.NextSibling {
		c.Rect = image.Rectangle{Max: c.MeasuredSize}
		c.Wrapper.Layout(t)
	}
}

func (m *ContainerEmbed) Paint(ctx *PaintContext, origin image.Point) error {
	m.Marks.UnmarkNeedsPaint()
	origin = origin.Add(m.Rect.Min)
	for c := m.FirstChild; c != nil; c = c.NextSibling {
		if err := c.Wrapper.Paint(ctx, origin); err != nil {
			return err
		}
	}
	return nil
}

func (m *ContainerEmbed) PaintBase(ctx *PaintBaseContext, origin image.Point) error {
	m.Marks.UnmarkNeedsPaintBase()
	origin = origin.Add(m.Rect.Min)
	for c := m.FirstChild; c != nil; c = c.NextSibling {
		if err := c.Wrapper.PaintBase(ctx, origin); err != nil {
			return err
		}
	}
	return nil
}

func (m *ContainerEmbed) OnChildMarked(child Node, newMarks Marks) {
	m.Mark(newMarks)
}

func (m *ContainerEmbed) OnLifecycleEvent(e lifecycle.Event) {
	for c := m.FirstChild; c != nil; c = c.NextSibling {
		c.Wrapper.OnLifecycleEvent(e)
	}
}

func (m *ContainerEmbed) OnInputEvent(e interface{}, origin image.Point) EventHandled {
	origin = origin.Add(m.Rect.Min)
	var p image.Point
	switch e := e.(type) {
	case gesture.Event:
		p = image.Point{
			X: int(e.CurrentPos.X) - origin.X,
			Y: int(e.CurrentPos.Y) - origin.Y,
		}
	case mouse.Event:
		p = image.Point{
			X: int(e.X) - origin.X,
			Y: int(e.Y) - origin.Y,
		}
	}
	// Iterate backwards. Later children have priority over earlier children,
	// as later ones are usually drawn over earlier ones.
	for c := m.LastChild; c != nil; c = c.PrevSibling {
		if p.In(c.Rect) && c.Wrapper.OnInputEvent(e, origin) == Handled {
			return Handled
		}
	}
	return NotHandled
}

// Embed is the common data structure for each node in a widget tree.
type Embed struct {
	// Wrapper is the outer type that wraps (embeds) this type. It should not
	// be nil.
	Wrapper Node

	// Parent, FirstChild, LastChild, PrevSibling and NextSibling describe the
	// widget tree structure.
	//
	// These fields are exported to enable walking the node tree, but they
	// should not be modified directly. Instead, call the Insert and Remove
	// methods, which keeps the tree structure consistent.
	Parent, FirstChild, LastChild, PrevSibling, NextSibling *Embed

	// LayoutData is layout-specific data for this node. Its type is determined
	// by its parent node's type. For example, each child of a Flow may hold a
	// FlowLayoutData in this field.
	LayoutData interface{}

	// TODO: add commentary about the Measure / Layout / Paint model, and about
	// the lifetime of the MeasuredSize and Rect fields, and when user code can
	// access and/or modify them. At some point a new cycle begins, a call to
	// measure is necessary, and using MeasuredSize is incorrect (unless you're
	// trying to recall something about the past).

	// MeasuredSize is the widget's natural size, in pixels, as calculated by
	// the most recent Measure call.
	MeasuredSize image.Point

	// Rect is the widget's position and actual (as opposed to natural) size,
	// in pixels, as calculated by the most recent Layout call on its parent
	// node. A parent may lay out a child at a size different to its natural
	// size in order to satisfy a layout constraint, such as a row of buttons
	// expanding to fill a panel's width.
	//
	// The position (Rectangle.Min) is relative to its parent node. This is not
	// necessarily the same as relative to the screen's, window's or image
	// buffer's origin.
	Rect image.Rectangle

	// Marks are a bitfield of node state, such as whether it needs measure,
	// layout or paint.
	Marks Marks
}

func (m *Embed) Wrappee() *Embed { return m }

// TODO: should insert and remove call Mark(MarkNeedsMeasureLayout | MarkNeedsPaint)?

func (m *Embed) insert(c, nextSibling Node) {
	n := c.Wrappee()
	if n.Parent != nil || n.PrevSibling != nil || n.NextSibling != nil {
		panic("node: Insert called for an attached child")
	}
	n.Parent = m

	if nextSibling == nil {
		last := m.LastChild
		if last != nil {
			last.NextSibling = n
		} else {
			m.FirstChild = n
		}
		m.LastChild = n
		n.PrevSibling = last
		return
	}

	o := nextSibling.Wrappee()
	if o.Parent != m {
		panic("node: Insert called for a non-sibling nextSibling node")
	}
	if o.PrevSibling == nil {
		o.PrevSibling = n
		n.NextSibling = o
		m.FirstChild = n
		return
	}

	o.PrevSibling.NextSibling = n
	n.PrevSibling = o.PrevSibling
	n.NextSibling = o
	o.PrevSibling = n
}

func (m *Embed) remove(c Node) {
	n := c.Wrappee()
	if n.Parent != m {
		panic("node: Remove called for a non-child node")
	}
	if m.FirstChild == n {
		m.FirstChild = n.NextSibling
	}
	if n.NextSibling != nil {
		n.NextSibling.PrevSibling = n.PrevSibling
	}
	if m.LastChild == n {
		m.LastChild = n.PrevSibling
	}
	if n.PrevSibling != nil {
		n.PrevSibling.NextSibling = n.NextSibling
	}
	n.Parent = nil
	n.PrevSibling = nil
	n.NextSibling = nil
}

func (m *Embed) Mark(marks Marks) {
	oldMarks := m.Marks
	m.Marks |= marks
	changedMarks := m.Marks ^ oldMarks
	if changedMarks != 0 && m.Parent != nil {
		m.Parent.Wrapper.OnChildMarked(m.Wrapper, changedMarks)
	}
}

// Marks are a bitfield of node state, such as whether it needs measure, layout
// or paint.
type Marks uint32

const (
	// MarkNeedsMeasureLayout marks this node as needing Measure and Layout
	// calls.
	MarkNeedsMeasureLayout = Marks(1 << 0)
	// TODO: use this.

	// MarkNeedsPaint marks this node as needing a Paint call.
	MarkNeedsPaint = Marks(1 << 1)

	// MarkNeedsPaintBase marks this node as needing a PaintBase call.
	MarkNeedsPaintBase = Marks(1 << 2)
)

func (m Marks) NeedsMeasureLayout() bool { return m&MarkNeedsMeasureLayout != 0 }
func (m Marks) NeedsPaint() bool         { return m&MarkNeedsPaint != 0 }
func (m Marks) NeedsPaintBase() bool     { return m&MarkNeedsPaintBase != 0 }

func (m *Marks) UnmarkNeedsMeasureLayout() { *m &^= MarkNeedsMeasureLayout }
func (m *Marks) UnmarkNeedsPaint()         { *m &^= MarkNeedsPaint }
func (m *Marks) UnmarkNeedsPaintBase()     { *m &^= MarkNeedsPaintBase }
