package cview

import (
	
	
)

// Focusable provides a method which determines if a primitive has focus.
// Composed primitives may be focused based on the focused state of their
// contained primitives.
type Focusable interface {
	HasFocus() bool
}

type focusElement struct {
	primitive Primitive
	disabled  bool
}

// FocusManager manages application focus.
type FocusManager struct {
	elements []*focusElement

	focused    int
	wrapAround bool

	setFocus func(p Primitive)

	sync.RWMutex
}

// NewFocusManager returns a new FocusManager object.
func ( func( Primitive)) *FocusManager {
	return &FocusManager{setFocus: }
}

// SetWrapAround sets the flag that determines whether navigation will wrap
// around. That is, navigating forwards on the last field will move the
// selection to the first field (similarly in the other direction). If set to
// false, the focus won't change when navigating forwards on the last element
// or navigating backwards on the first element.
func ( *FocusManager) ( bool) {
	.Lock()
	defer .Unlock()

	.wrapAround = 
}

// Add adds an element to the focus handler.
func ( *FocusManager) ( ...Primitive) {
	.Lock()
	defer .Unlock()

	for ,  := range  {
		.elements = append(.elements, &focusElement{primitive: })
	}
}

// AddAt adds an element to the focus handler at the specified index.
func ( *FocusManager) ( int,  Primitive) {
	.Lock()
	defer .Unlock()

	if  < 0 ||  > len(.elements) {
		panic("index out of range")
	}

	 := &focusElement{primitive: }

	if  == len(.elements) {
		.elements = append(.elements, )
		return
	}
	.elements = append(.elements[:+1], .elements[:]...)
	.elements[] = 
}

// Focus focuses the provided element.
func ( *FocusManager) ( Primitive) {
	.Lock()
	defer .Unlock()

	if len(.elements) == 0 {
		return
	}

	for ,  := range .elements {
		if  == .primitive && !.disabled {
			.focused = 
			break
		}
	}
	.setFocus(.elements[.focused].primitive)
}

// FocusPrevious focuses the previous element.
func ( *FocusManager) () {
	.Lock()
	defer .Unlock()

	.focused--
	.updateFocusIndex(true)
	.setFocus(.elements[.focused].primitive)
}

// FocusNext focuses the next element.
func ( *FocusManager) () {
	.Lock()
	defer .Unlock()

	.focused++
	.updateFocusIndex(false)
	.setFocus(.elements[.focused].primitive)
}

// FocusAt focuses the element at the provided index.
func ( *FocusManager) ( int) {
	.Lock()
	defer .Unlock()

	.focused = 
	.setFocus(.elements[.focused].primitive)
}

// GetFocusIndex returns the index of the currently focused element.
func ( *FocusManager) () int {
	.Lock()
	defer .Unlock()

	return .focused
}

func ( *FocusManager) ( int) error {
	// TODO
	// f.Lock()
	// defer f.Unlock()

	if  < 0 ||  >= len(.elements) {
		return errors.New("index out of range")
	}

	.focused = 
	return nil
}

// GetFocusedPrimitive returns the currently focused primitive.
func ( *FocusManager) () Primitive {
	.Lock()
	defer .Unlock()

	return .elements[.focused].primitive
}

func ( *FocusManager) ( bool) {
	for  := 0;  < len(.elements); ++ {
		if .focused < 0 {
			if .wrapAround {
				.focused = len(.elements) - 1
			} else {
				.focused = 0
			}
		} else if .focused >= len(.elements) {
			if .wrapAround {
				.focused = 0
			} else {
				.focused = len(.elements) - 1
			}
		}

		 := .elements[.focused]
		if !.disabled {
			break
		}

		if  {
			.focused--
		} else {
			.focused++
		}
	}
}

// Transform modifies the current focus.
func ( *FocusManager) ( Transformation) {
	var  bool
	switch  {
	case TransformFirstItem:
		.focused = 0
		 = true
	case TransformLastItem:
		.focused = len(.elements) - 1
	case TransformPreviousItem:
		.focused--
		 = true
	case TransformNextItem:
		.focused++
	}
	.updateFocusIndex()
}

// Reset resets the focus manager.
func ( *FocusManager) () {
	.Lock()
	defer .Unlock()

	.focused = 0
	.elements = nil
}

// Len returns the length of added primitives.
func ( *FocusManager) () int {
	.Lock()
	defer .Unlock()

	return len(.elements)
}