package clinked

import (
	"log"
	"strings"

	"github.com/davecgh/go-spew/spew"
)

// CNode 循环链表 三色标记 不确定是否会清除循环引用, 网上说会
type CNode struct {
	value interface{}
	prev  *CNode
	next  *CNode
}

// GetValue 获取到Node的值
func (node *CNode) GetValue() interface{} {
	return node.value
}

// SetValue 获取到Node的值
func (node *CNode) SetValue(value interface{}) {
	node.value = value
}

// CircularLinked 循环链表
type CircularLinked struct {
	cursor *CNode
	head   *CNode
	tail   *CNode
	size   uint64
}

// NewCircularLinked create a CircularLinked
func NewCircularLinked(values ...interface{}) *CircularLinked {
	list := &CircularLinked{}
	if len(values) > 0 {
		list.Append(values...)
	}
	return list
}

// Head 链表的首
func (list *CircularLinked) Head() *CNode {
	return list.head
}

// Tail 链表的首
func (list *CircularLinked) Tail() *CNode {
	return list.tail
}

// Cursor get current Cursor
func (list *CircularLinked) Cursor() *CNode {
	if list.cursor == nil {
		list.cursor = list.head
	}
	return list.cursor
}

// MoveNext get next Cursor
func (list *CircularLinked) MoveNext() *CNode {
	if list.cursor == nil {
		list.cursor = list.head
	}
	list.cursor = list.cursor.next
	return list.cursor
}

// MovePrev get prev Cursor
func (list *CircularLinked) MovePrev() *CNode {
	if list.cursor == nil {
		list.cursor = list.head
	}
	list.cursor = list.cursor.prev
	return list.cursor
}

// CursorToHead cursor move to head
func (list *CircularLinked) CursorToHead() *CNode {
	list.cursor = list.head
	return list.cursor
}

// CursorToTail cursor move to tail
func (list *CircularLinked) CursorToTail() *CNode {
	list.cursor = list.tail
	return list.cursor
}

// GetLoopValues 获取从头到尾的值
func (list *CircularLinked) GetLoopValues() []*CNode {
	var result []*CNode

	if list.head != nil {
		result = append(result, list.head)
		for cur := list.head.next; cur != list.head; cur = cur.next {
			result = append(result, cur)
		}
	}
	return result
}

// Append a value (one or more) at the end of the list (same as Append())
func (list *CircularLinked) Append(values ...interface{}) {
	for _, value := range values {
		node := &CNode{value: value}
		if list.size == 0 {
			list.head = node
			list.tail = node
			node.next = node
			node.prev = node
		} else {
			list.tail.next = node
			node.next = list.head
			node.prev = list.tail
			list.tail = node
		}
		list.size++
	}
}

// Remove 移除一些节点
func (list *CircularLinked) Remove(node *CNode) {

	switch list.size {
	case 0:
		list.errorNotInList(node)
	case 1:
		if list.head == node {
			list.head = nil
			list.tail = nil
			node.next = nil
			node.prev = nil
			list.cursor = nil
			list.size--
		} else {
			list.errorNotInList(node)
		}
	case 2:

		node.prev = nil
		node.next = nil

		switch node {
		case list.head:
			list.head = list.tail
			list.tail.prev = list.head
			list.head.next = list.tail
			list.cursor = list.head
			list.size--
		case list.tail:
			list.tail = list.head
			list.tail.prev = list.head
			list.head.next = list.tail
			list.cursor = list.head
			list.size--
		default:
			list.errorNotInList(node)
		}

	default:
		switch node {
		case list.head:
			_, next := list.cutAndSplice(node)
			list.size--
			list.head = next
			if list.cursor == node {
				list.cursor = next
			}
		case list.tail:
			prev, _ := list.cutAndSplice(node)
			list.size--
			list.tail = prev
			if list.cursor == node {
				list.cursor = prev
			}
		default:
			_, next := list.cutAndSplice(node)
			list.size--
			if list.cursor == node {
				list.cursor = next
			}
		}
	}

}

// LookCursor for list show
func (list *CircularLinked) LookCursor() string {
	cursor := list.Cursor()

	content := "->["
	cur := list.head
	if list.size != 0 {
		for size := uint64(0); size < list.size; size++ {
			if cursor == cur {
				content += "(" + spew.Sprint(cur.value) + ")" + ", "
			} else {
				content += spew.Sprint(cur.value) + ", "
			}

			cur = cur.next
		}
	}
	content = strings.TrimRight(content, ", ")
	showlen := len(content)
	if showlen >= 64 {
		showlen = 64
	}
	content += "]" + content[0:showlen] + " ..."
	return content
}

// Clear for list show
func (list *CircularLinked) Clear() {
	if list.size != 0 {
		list.head.prev = nil
		list.tail.next = nil
		list.head = nil
		list.tail = nil
		list.cursor = nil
		list.size = 0
	}
}

// Size for list show
func (list *CircularLinked) Size() uint64 {
	return list.size
}

func (list *CircularLinked) errorNotInList(node *CNode) {
	log.Println("the node value ", spew.Sprint(node), " is not in list")
}

// cutAndSplice (不考虑边界情况)
func (list *CircularLinked) cutAndSplice(node *CNode) (prev, next *CNode) {
	prev = node.prev
	next = node.next

	prev.next = next
	next.prev = prev

	node.prev = nil
	node.next = nil

	return prev, next
}
