package cview

import (
	

	
)

// Configuration values.
const (
	ScrollRow = iota
	ScrollColumn
)

// scrollItem holds layout options for one item.
type scrollItem struct {
	Item      Primitive // The item to be positioned. May be nil for an empty item.
	FixedSize int       // The item's fixed size which may not be changed, 0 if it has no fixed size.
	Focus     bool      // Whether or not this item attracts the layout's focus.
}

// ScrollView is a basic implementation of the Scrollbox layout. The contained
// primitives are arranged horizontally or vertically. The way they are
// distributed along that dimension depends on their layout settings, which is
// either a fixed length or a proportional length. See AddItem() for details.
type ScrollView struct {
	*Box

	// The items to be positioned.
	items []*scrollItem

	// // ScrollRow or ScrollColumn.
	// direction int

	// If set to true, ScrollView will use the entire screen as its available space
	// instead its box dimensions.
	fullScreen bool

	// Visibility of the scroll bar.
	scrollBarVisibility ScrollBarVisibility

	// The scroll bar color.
	scrollBarColor tcell.Color

	// The number of characters to be skipped on each line (not in wrap mode).
	heightOffset int

	sync.RWMutex
}

// NewScrollView returns a new scrollbox layout container with no primitives and its
// direction set to ScrollColumn. To add primitives to this layout, see AddItem().
// To change the direction, see SetDirection().
//
// Note that ScrollView will have a transparent background by default so that any nil
// scroll items will show primitives behind the ScrollView.
// To disable this transparency:
//
//	scroll.SetBackgroundTransparent(false)
func () *ScrollView {
	 := &ScrollView{
		Box:                 NewBox(),
		scrollBarVisibility: ScrollBarAuto,
		scrollBarColor:      Styles.ScrollBarColor,
	}
	.SetBackgroundTransparent(true)
	.focus = 
	return 
}

// SetScrollBarVisibility specifies the display of the scroll bar.
func ( *ScrollView) ( ScrollBarVisibility) {
	.Lock()
	defer .Unlock()

	.scrollBarVisibility = 
}

// SetScrollBarColor sets the color of the scroll bar.
func ( *ScrollView) ( tcell.Color) {
	.Lock()
	defer .Unlock()

	.scrollBarColor = 
}

// SetFullScreen sets the flag which, when true, causes the scroll layout to use
// the entire screen space instead of whatever size it is currently assigned to.
func ( *ScrollView) ( bool) {
	.Lock()
	defer .Unlock()

	.fullScreen = 
}

// GetItems returns a slice of all items in the container.
func ( *ScrollView) () []Primitive {
	.RLock()
	defer .RUnlock()
	var  []Primitive
	for ,  := range .items {
		 = append(, .Item)
	}

	return 
}

// AddItem adds a new item to the container. The "fixedSize" argument is a width
// or height that may not be changed by the layout algorithm. A value of 0 means
// that its size is scrollible and may be changed. The "proportion" argument
// defines the relative size of the item compared to other scrollible-size items.
// For example, items with a proportion of 2 will be twice as large as items
// with a proportion of 1. The proportion must be at least 1 if fixedSize == 0
// (ignored otherwise).
//
// If "focus" is set to true, the item will receive focus when the ScrollView
// primitive receives focus. If multiple items have the "focus" flag set to
// true, the first one will receive focus.
//
// A nil value for the primitive represents empty space.
func ( *ScrollView) ( Primitive,  int,  bool) {
	.Lock()
	defer .Unlock()

	if  == nil {
		 = NewBox()
		.SetVisible(false)
	}

	.items = append(.items, &scrollItem{Item: , FixedSize: , Focus: })
}

// AddItemAtIndex adds an item to the scroll at a given index.
// For more information see AddItem.
func ( *ScrollView) ( int,  Primitive,  int,  bool) {
	.Lock()
	defer .Unlock()
	 := &scrollItem{Item: , FixedSize: , Focus: }

	if  == 0 {
		.items = append([]*scrollItem{}, .items...)
	} else {
		.items = append(.items[:], append([]*scrollItem{}, .items[:]...)...)
	}
}

// RemoveItem removes all items for the given primitive from the container,
// keeping the order of the remaining items intact.
func ( *ScrollView) ( Primitive) {
	.Lock()
	defer .Unlock()

	for  := len(.items) - 1;  >= 0; -- {
		if .items[].Item ==  {
			.items = append(.items[:], .items[+1:]...)
		}
	}
}

// ResizeItem sets a new size for the item(s) with the given primitive. If there
// are multiple ScrollView items with the same primitive, they will all receive the
// same size. For details regarding the size parameters, see AddItem().
func ( *ScrollView) ( Primitive,  int) {
	.Lock()
	defer .Unlock()

	for ,  := range .items {
		if .Item ==  {
			.FixedSize = 
		}
	}
}

// Draw draws this primitive onto the screen.
func ( *ScrollView) ( tcell.Screen) {
	if !.GetVisible() {
		return
	}

	.Box.Draw()

	.Lock()
	defer .Unlock()

	// Calculate size and position of the items.

	// Do we use the entire screen?
	if .fullScreen {
		,  := .Size()
		.SetRect(0, 0, , )
	}
	// How much space can we distribute?
	, , ,  := .GetInnerRect()

	// How tall is the content?
	 := 0
	for ,  := range .items {
		 += .FixedSize
	}
	if  >  && +.heightOffset+ > + {
		.heightOffset =  - 
	}

	 := .scrollBarVisibility == ScrollBarAlways || (.scrollBarVisibility == ScrollBarAuto &&  > )
	if  > 0 &&  {
		-- // Subtract space for scroll bar.
	}

	// draw
	 := 
	 := 
	 := -1

	// TODO scroll to focused one when not visible
	for ,  := range .items {
		 := .FixedSize

		// scrolled
		if  < +.heightOffset &&  {
			 += 
			continue
		}
		if  == -1 {
			 = 
		}
		 += 

		.Item.SetRect(, , , )
		 += 

		if .Item != nil {
			if .Item.GetFocusable().HasFocus() {
				defer .Item.Draw()
			} else {
				.Item.Draw()
			}
		}
	}

	// fill the remaining space
	if  < + {
		for  := ;  < +; ++ {
			for  := 0;  < ; ++ {
				.SetContent(, , ' ', nil, tcell.StyleDefault.Background(Styles.PrimitiveBackgroundColor))
			}
		}
	}

	if ! {
		return
	}

	// Draw scroll bar last.
	defer func() {

		 := int(float64() * (float64(-) / float64(-)))
		if  >  {
			 = 
		}

		for  := 0;  < ; ++ {
			RenderScrollBar(, .scrollBarVisibility, +, +, , , , , .hasFocus, .scrollBarColor)
		}
	}()
}

// ScrollTo scrolls to the specified height and width (both starting with 0).
func ( *ScrollView) (,  int) {
	.Lock()
	defer .Unlock()

	.heightOffset = 
	// t.columnOffset = column
	// t.trackEnd = false
}

// Focus is called when this primitive receives focus.
func ( *ScrollView) ( func( Primitive)) {
	.Lock()

	for ,  := range .items {
		if .Item != nil && .Focus {
			.Unlock()
			(.Item)
			return
		}
	}

	.Unlock()
}

// HasFocus returns whether or not this primitive has focus.
func ( *ScrollView) () bool {
	.RLock()
	defer .RUnlock()

	for ,  := range .items {
		if .Item != nil && .Item.GetFocusable().HasFocus() {
			return true
		}
	}
	return false
}

// MouseHandler returns the mouse handler for this primitive.
func ( *ScrollView) () func( MouseAction,  *tcell.EventMouse,  func( Primitive)) ( bool,  Primitive) {
	return .WrapMouseHandler(func( MouseAction,  *tcell.EventMouse,  func( Primitive)) ( bool,  Primitive) {
		if !.InRect(.Position()) {
			return false, nil
		}

		switch  {
		case MouseScrollUp:
			.heightOffset--
			if .heightOffset < 0 {
				.heightOffset = 0
			}
			 = true
		case MouseScrollDown:
			.heightOffset++
			 = true
		}

		if  {
			return
		}

		// Pass mouse events along to the first child item that takes it.
		for ,  := range .items {
			if .Item == nil {
				continue
			}

			,  = .Item.MouseHandler()(, , )
			if  {
				return
			}
		}

		return
	})
}