package middleware

import 

// RelativePosition provides specifying the relative position of a middleware
// in an ordered group.
type RelativePosition int

// Relative position for middleware in steps.
const (
	After RelativePosition = iota
	Before
)

type ider interface {
	ID() string
}

// orderedIDs provides an ordered collection of items with relative ordering
// by name.
type orderedIDs struct {
	order *relativeOrder
	items map[string]ider
}

const baseOrderedItems = 5

func () *orderedIDs {
	return &orderedIDs{
		order: newRelativeOrder(),
		items: make(map[string]ider, baseOrderedItems),
	}
}

// Add injects the item to the relative position of the item group. Returns an
// error if the item already exists.
func ( *orderedIDs) ( ider,  RelativePosition) error {
	 := .ID()
	if len() == 0 {
		return fmt.Errorf("empty ID, ID must not be empty")
	}

	if  := .order.Add(, );  != nil {
		return 
	}

	.items[] = 
	return nil
}

// Insert injects the item relative to an existing item id. Returns an error if
// the original item does not exist, or the item being added already exists.
func ( *orderedIDs) ( ider,  string,  RelativePosition) error {
	if len(.ID()) == 0 {
		return fmt.Errorf("insert ID must not be empty")
	}
	if len() == 0 {
		return fmt.Errorf("relative to ID must not be empty")
	}

	if  := .order.Insert(, , .ID());  != nil {
		return 
	}

	.items[.ID()] = 
	return nil
}

// Get returns the ider identified by id. If ider is not present, returns false.
func ( *orderedIDs) ( string) (ider, bool) {
	,  := .items[]
	return , 
}

// Swap removes the item by id, replacing it with the new item. Returns an error
// if the original item doesn't exist.
func ( *orderedIDs) ( string,  ider) (ider, error) {
	if len() == 0 {
		return nil, fmt.Errorf("swap from ID must not be empty")
	}

	 := .ID()
	if len() == 0 {
		return nil, fmt.Errorf("swap to ID must not be empty")
	}

	if  := .order.Swap(, );  != nil {
		return nil, 
	}

	 := .items[]

	delete(.items, )
	.items[] = 

	return , nil
}

// Remove removes the item by id. Returns an error if the item
// doesn't exist.
func ( *orderedIDs) ( string) (ider, error) {
	if len() == 0 {
		return nil, fmt.Errorf("remove ID must not be empty")
	}

	if  := .order.Remove();  != nil {
		return nil, 
	}

	 := .items[]
	delete(.items, )
	return , nil
}

func ( *orderedIDs) () []string {
	 := .order.List()
	 := make([]string, len())
	copy(, )
	return 
}

// Clear removes all entries and slots.
func ( *orderedIDs) () {
	.order.Clear()
	.items = map[string]ider{}
}

// GetOrder returns the item in the order it should be invoked in.
func ( *orderedIDs) () []interface{} {
	 := .order.List()
	 := make([]interface{}, len())
	for  := 0;  < len(); ++ {
		[] = .items[[]]
	}

	return 
}

// relativeOrder provides ordering of item
type relativeOrder struct {
	order []string
}

func () *relativeOrder {
	return &relativeOrder{
		order: make([]string, 0, baseOrderedItems),
	}
}

// Add inserts an item into the order relative to the position provided.
func ( *relativeOrder) ( RelativePosition,  ...string) error {
	if len() == 0 {
		return nil
	}

	for ,  := range  {
		if ,  := .has();  {
			return fmt.Errorf("already exists, %v", )
		}
	}

	switch  {
	case Before:
		return .insert(0, Before, ...)

	case After:
		.order = append(.order, ...)

	default:
		return fmt.Errorf("invalid position, %v", int())
	}

	return nil
}

// Insert injects an item before or after the relative item. Returns
// an error if the relative item does not exist.
func ( *relativeOrder) ( string,  RelativePosition,  ...string) error {
	if len() == 0 {
		return nil
	}

	for ,  := range  {
		if ,  := .has();  {
			return fmt.Errorf("already exists, %v", )
		}
	}

	,  := .has()
	if ! {
		return fmt.Errorf("not found, %v", )
	}

	return .insert(, , ...)
}

// Swap will replace the item id with the to item. Returns an
// error if the original item id does not exist. Allows swapping out an
// item for another item with the same id.
func ( *relativeOrder) (,  string) error {
	,  := .has()
	if ! {
		return fmt.Errorf("not found, %v", )
	}

	if _,  = .has();  &&  !=  {
		return fmt.Errorf("already exists, %v", )
	}

	.order[] = 
	return nil
}

func ( *relativeOrder) ( string) error {
	,  := .has()
	if ! {
		return fmt.Errorf("not found, %v", )
	}

	.order = append(.order[:], .order[+1:]...)
	return nil
}

func ( *relativeOrder) () []string {
	return .order
}

func ( *relativeOrder) () {
	.order = .order[0:0]
}

func ( *relativeOrder) ( int,  RelativePosition,  ...string) error {
	switch  {
	case Before:
		 := len()
		var  []string
		if  <= cap(.order)-len(.order) {
			.order = .order[:len(.order)+]
			 = .order
		} else {
			 = .order
			.order = make([]string, len(.order)+)
			copy(.order[:], [:]) // only when allocating a new slice do we need to copy the front half
		}
		copy(.order[+:], [:])
		copy(.order[:], )
	case After:
		if  == len(.order)-1 || len(.order) == 0 {
			.order = append(.order, ...)
		} else {
			.order = append(.order[:+1], append(, .order[+1:]...)...)
		}

	default:
		return fmt.Errorf("invalid position, %v", int())
	}

	return nil
}

func ( *relativeOrder) ( string) ( int,  bool) {
	for  := 0;  < len(.order); ++ {
		if .order[] ==  {
			return , true
		}
	}
	return 0, false
}