package plist

import (
	"strings"

	"github.com/davecgh/go-spew/spew"
	"github.com/emirpasic/gods/utils"
)

type Node struct {
	prev, next *Node
	value      interface{}
}

type PriorityList struct {
	head, tail *Node
	size       int
	comparator utils.Comparator
}

func New(compartor utils.Comparator) *PriorityList {
	pl := &PriorityList{head: &Node{}, tail: &Node{}, size: 0, comparator: compartor}
	pl.head.next = pl.tail
	pl.tail.prev = pl.head
	return pl
}

func (pl *PriorityList) String() string {
	content := ""

	cur := pl.head.next

	for ; cur != pl.tail; cur = cur.next {
		content += spew.Sprint(cur.value) + " "
	}
	content = strings.TrimRight(content, " ")
	return content
}

func (pl *PriorityList) RString() string {
	content := ""

	cur := pl.tail.prev

	for ; cur != pl.head; cur = cur.prev {
		content += spew.Sprint(cur.value) + " "
	}
	content = strings.TrimRight(content, " ")
	return content
}

func (pl *PriorityList) Iterator() *Iterator {
	return &Iterator{pl: pl, cur: pl.head}
}

func (pl *PriorityList) CircularIterator() *CircularIterator {
	return &CircularIterator{pl: pl, cur: pl.head}
}

func (pl *PriorityList) Push(pvalue interface{}) {
	pl.size++
	pnode := &Node{value: pvalue}
	if pl.size == 1 {
		pl.head.next = pnode
		pl.tail.prev = pnode
		pnode.prev = pl.head
		pnode.next = pl.tail
		return
	}

	cur := pl.head
	for ; cur.next != pl.tail; cur = cur.next {
		if pl.comparator(pvalue, cur.next.value) > 0 {
			cnext := cur.next

			cur.next = pnode
			cnext.prev = pnode
			pnode.prev = cur
			pnode.next = cnext

			return
		}
	}

	cur.next = pnode
	pnode.prev = cur
	pnode.next = pl.tail
	pl.tail.prev = pnode
}

func (pl *PriorityList) Get(idx int) interface{} {
	return pl.GetNode(idx).value
}

func (pl *PriorityList) GetNode(idx int) *Node {
	if idx >= 0 {
		cur := pl.head.next
		for i := 0; cur != pl.tail; i++ {
			if i == idx {
				return cur
			}
			cur = cur.next
		}
	} else {
		cur := pl.tail.prev
		for i := -1; cur != pl.head; i-- {
			if i == idx {
				return cur
			}
			cur = cur.prev
		}
	}
	return nil
}

func (pl *PriorityList) RemoveWithIndex(idx int) {
	pl.Remove(pl.GetNode(idx))
}

func (pl *PriorityList) Remove(node *Node) {

	prev := node.prev
	next := node.next
	prev.next = next
	next.prev = prev

	node.prev = nil
	node.next = nil

	pl.size--
}
func (pl *PriorityList) Values() []interface{} {
	values := make([]interface{}, pl.size, pl.size)
	for i, cur := 0, pl.head.next; cur != pl.tail; i, cur = i+1, cur.next {
		values[i] = cur.value
	}
	return values
}