package tk

import 

// The number of lines the listing mode keeps between the current selected item
// and the top and bottom edges of the window, unless the available height is
// too small or if the selected item is near the top or bottom of the list.
var respectDistance = 2

// Determines the index of the first item to show in vertical mode.
//
// This function does not return the full window, but just the first item to
// show, and how many initial lines to crop. The window determined by this
// algorithm has the following properties:
//
// * It always includes the selected item.
//
// * The combined height of all the entries in the window is equal to
//   min(height, combined height of all entries).
//
// * There are at least respectDistance rows above the first row of the selected
//   item, as well as that many rows below the last row of the selected item,
//   unless the height is too small.
//
// * Among all values satisfying the above conditions, the value of first is
//   the one closest to lastFirst.
func ( ListBoxState,  int) (,  int) {
	, ,  := .Items, .Selected, .First
	 := .Len()
	if  < 0 {
		 = 0
	} else if  >=  {
		 =  - 1
	}
	 := .Show().CountLines()

	if  <=  {
		// The height is not big enough (or just big enough) to fit the selected
		// item. Fit as much as the selected item as we can.
		return , 0
	}

	// Determine the minimum amount of space required for the downward direction.
	 :=  - 
	var  int
	if  >= 2*respectDistance {
		// If we can afford maintaining the respect distance on both sides, then
		// the minimum amount of space required is the respect distance.
		 = respectDistance
	} else {
		// Otherwise we split the available space by half. The downward (no pun
		// intended) rounding here is an arbitrary choice.
		 =  / 2
	}
	// Calculate how much of the budget the downward direction can use. This is
	// used to 1) potentially shrink needDown 2) decide how much to expand
	// upward later.
	 := 0
	for  :=  + 1;  < ; ++ {
		 += .Show().CountLines()
		if  >=  {
			break
		}
	}
	if  >  {
		// We reached the last item without using all of needDown. That means we
		// don't need so much in the downward direction.
		 = 
	}

	// The maximum amount of space we can use in the upward direction is the
	// entire budget minus the minimum amount of space we need in the downward
	// direction.
	 :=  - 

	 := 0
	// Extend upwards until any of the following becomes true:
	//
	// * We have exhausted budgetUp;
	//
	// * We have reached item 0;
	//
	// * We have reached or passed lastFirst, satisfied the upward respect
	//   distance, and will be able to use up the entire budget when expanding
	//   downwards later.
	for  :=  - 1;  >= 0; -- {
		 += .Show().CountLines()
		if  >=  {
			return ,  - 
		}
		if  <=  &&  >= respectDistance && + >=  {
			return , 0
		}
	}
	return 0, 0
}

// Determines the window to show in horizontal  It returns the first item
// to show and the amount of height required.
func ( ListBoxState, , ,  int) (int, int) {
	 := .Items
	 := .Len()
	// Lower bound of number of items that can fit in a row.
	 := ( + listBoxColGap) / (maxWidth(, , 0, ) + listBoxColGap)
	if  == 0 {
		// We trim items that are too wide, so there is at least one item per row.
		 = 1
	}
	if * >=  {
		// All items can fit.
		return 0, ( +  - 1) / 
	}
	// Reduce the amount of available height by one because the last row will be
	// reserved for the scrollbar.
	--
	,  := .Selected, .First
	// Start with the column containing the selected item, move left until
	// either the width is exhausted, or lastFirst has been reached.
	 :=  /  * 
	 := maxWidth(, , , +)
	for ;  > ;  -=  {
		 += maxWidth(, , -, ) + listBoxColGap
		if  >  {
			break
		}
	}
	return , 
}

func ( Items, , ,  int) int {
	 := .Len()
	 := 0
	for  := ;  <  &&  < ; ++ {
		 := 0
		for ,  := range .Show() {
			 += wcwidth.Of(.Text)
		}
		if  <  {
			 = 
		}
	}
	return  + 2*
}