// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.

// linked-list.go
package list

import (
	"fmt"

	"git.portale-stac.it/go-pkg/expr/kern"
)

type ListNode struct {
	data any
	next *ListNode
}

func (node *ListNode) Next() *ListNode {
	return node.next
}

func (node *ListNode) MoveOn(steps int64) *ListNode {
	n := node
	for i := int64(0); n != nil && i < steps; i++ {
		n = n.Next()
	}
	return n
}

func (self *ListNode) Data() any {
	return self.data
}

type LinkedList struct {
	count int
	first *ListNode
	last  *ListNode
}

func NewLinkedList() (list *LinkedList) {
	list = &LinkedList{0, nil, nil}
	return
}

func (ls *LinkedList) Len() int {
	return ls.count
}

func (ls *LinkedList) Empty() bool {
	return ls.count == 0
}

func (ls *LinkedList) PushNodeFront(node *ListNode) *ListNode {
	node.next = ls.first
	ls.first = node
	if ls.last == nil {
		ls.last = ls.first
	}
	ls.count++
	return node
}

func (ls *LinkedList) PushNodeBack(node *ListNode) *ListNode {
	node.next = nil
	if ls.last != nil {
		ls.last.next = node
	}
	ls.last = node
	if ls.first == nil {
		ls.first = node
	}
	ls.count++
	return node
}

func (ls *LinkedList) PushFront(data any) *ListNode {
	ls.first = &ListNode{data, ls.first}
	if ls.last == nil {
		ls.last = ls.first
	}
	ls.count++
	return ls.first
}

func (ls *LinkedList) SeqPushFront(data any) (result *LinkedList) {
	switch seq := data.(type) {
	case kern.Iterator:
		result = ls.IterPushFront(seq)
	default:
		ls.PushFront(data)
		result = ls
	}
	return
}

func (ls *LinkedList) IterPushFront(it kern.Iterator) *LinkedList {
	for item, err1 := it.Next(); err1 == nil; item, err1 = it.Next() {
		ls.PushFront(item)
	}
	return ls
}

func (ls *LinkedList) PushBack(data any) (node *ListNode) {
	node = &ListNode{data, nil}
	if ls.last != nil {
		ls.last.next = node
	}
	ls.last = node
	if ls.first == nil {
		ls.first = node
	}
	ls.count++
	return
}

func (ls *LinkedList) SeqPushBack(data any) (result *LinkedList) {
	switch seq := data.(type) {
	case kern.Iterator:
		result = ls.IterPushBack(seq, false)
	default:
		ls.PushBack(data)
		result = ls
	}
	return
}

func (ls *LinkedList) SeqClonePushBack(data any) (result *LinkedList) {
	switch seq := data.(type) {
	case kern.Iterator:
		result = ls.IterPushBack(seq, true)
	default:
		ls.PushBack(data)
		result = ls
	}
	return
}

func (ls *LinkedList) IterPushBack(it kern.Iterator, clone bool) *LinkedList {
	for item, err1 := it.Next(); err1 == nil; item, err1 = it.Next() {
		if clone && kern.IsCloner(item) {
			item = item.(kern.Cloner).Clone()
		} else {
			ls.PushBack(item)
		}
	}
	return ls
}

func (ls *LinkedList) FirstNode() *ListNode {
	return ls.first
}

func (ls *LinkedList) First() (data any, err error) {
	if ls.first != nil {
		data = ls.first.data
	} else {
		err = errorListEmpty()
	}
	return
}

func (ls *LinkedList) LastNode() *ListNode {
	return ls.last
}

func (ls *LinkedList) Last() (data any, err error) {
	if ls.last != nil {
		data = ls.last.data
	} else {
		err = errorListEmpty()
	}
	return
}

// func (ls *LinkedList) Reverse() *ListNode {
// 	var prev *ListNode

// 	for current := ls.first; current != nil; {
// 		succ := current.next
// 		prev = succ.next
// 		succ.next = current
// 		current = prev
// 	}
// 	ls.first, ls.last = ls.last, ls.first
// 	return ls.first
// }

func (ls *LinkedList) NodeAt(index64 int64) (node *ListNode, err error) {
	index := int(index64)
	if ls.count == 0 {
		err = errorListEmpty()
	} else if index > ls.count {
		err = errorOutOfRange()
	} else if index == ls.count-1 {
		node = ls.last
	} else {
		current := ls.first
		for range index {
			current = current.next
		}
		node = current
	}
	return
}

func (ls *LinkedList) At(index int64) (data any, err error) {
	node, err := ls.NodeAt(index)
	if err == nil {
		data = node.data
	}
	return
}

func (ls *LinkedList) SetItem(index int64, data any) (err error) {
	node, err := ls.NodeAt(index)
	if err == nil {
		node.data = data
	}
	return
}

func (ls *LinkedList) Insert(index int, data any) *ListNode {
	var prev *ListNode

	prev = nil
	current := ls.first
	for pos := 0; current != nil && pos < index; pos++ {
		prev = current
		current = current.next
	}

	node := &ListNode{data, current}

	if prev != nil {
		prev.next = node
	}

	if ls.first == current {
		ls.first = node
	}
	if node.next == nil {
		ls.last = node
	}
	ls.count++
	return node
}

func (ls *LinkedList) PushBackStringArray(items []string) {
	for _, v := range items {
		ls.PushBack(v)
	}
}

func (ls *LinkedList) Sub(start, end int64) (subList *LinkedList) {
	subList = NewLinkedList()
	if node, err := ls.NodeAt(start); err == nil {
		for i := start; i < end && node != nil; i++ {
			subList.PushBack(node.data)
			node = node.next
		}
	}
	return
}

func (ls *LinkedList) SubStep(start, end, step int64) (subList *LinkedList) {
	subList = NewLinkedList()
	if node, err := ls.NodeAt(start); err == nil {
		for i := start; i < end && node != nil; i += step {
			subList.PushBack(node.data)
			node = node.MoveOn(step)
		}
	}
	return
}

// func (ls *LinkedList) SubStep(start, end, step int64) (subList *LinkedList) {
// 	subList = NewLinkedList()
// 	if node, err := ls.NodeAt(start); err == nil {
// 		forward := step > 0
// 		if !forward {
// 			step = -step
// 		}
// 		for i := start; i < end && node != nil; i += step {
// 			if forward {
// 				subList.PushBack(node.data)
// 			} else {
// 				subList.PushFront(node.data)
// 			}
// 			node = node.MoveOn(step)
// 		}
// 	}
// 	return
// }

// type TraverseOperator func(index int, elem interface{}, userData interface{}) (err error)

// func (self *LinkedList) Traverse(op TraverseOperator, user_data interface{}) (err error) {
// 	index := int(0)
// 	for current := self.first; current != nil; current = current.next {
// 		err = op(index, current.data, user_data)
// 		if err != nil {
// 			break
// 		}
// 		index++
// 	}
// 	return
// }

// func (self *LinkedList) Traverse2(observer Observer, abortOnError bool) (err error) {
// 	index := int(0)
// 	for current := self.first; current != nil; current = current.next {
// 		err = observer.Observe(current, index)
// 		if err != nil && abortOnError {
// 			break
// 		}
// 		index++
// 	}
// 	return
// }

type EqualFunc func(current, target any) bool

func (ls *LinkedList) FindFirst(eqFunc EqualFunc, targetData any) (targetNode *ListNode) {
	for current := ls.first; current != nil && targetNode == nil; current = current.next {
		if eqFunc(current.data, targetData) {
			targetNode = current
		}
	}
	return
}

func (ls *LinkedList) FindNext(eqFunc EqualFunc, startNode *ListNode) (targetNode *ListNode) {
	if startNode != nil {
		for current := startNode.next; current != nil && targetNode == nil; current = current.next {
			if eqFunc(current.data, startNode.Data()) {
				targetNode = current
			}
		}
	}
	return
}

func (ls *LinkedList) Revert() (rls *LinkedList) {
	var next *ListNode
	node := ls.first
	ls.count = 0
	ls.first = nil
	ls.last = nil
	for ; node != nil; node = next {
		next = node.next
		ls.PushNodeFront(node)
	}
	return ls
}

// type DataFeeder func(user_data interface{}) interface{}
// type NodeObserver func(node *ListNode, index int, userData interface{})

// func (self *LinkedList) FeedTail(feeder DataFeeder, feederUserData interface{}, observer NodeObserver, observerUserData interface{}) (count int) {
// 	count = 0
// 	for item := feeder(feederUserData); item != nil; item = feeder(feederUserData) {
// 		// fmt.Println("Item", count, item)
// 		node := self.PushBack(item)
// 		if observer != nil {
// 			observer(node, count, observerUserData)
// 		}
// 		count++
// 	}
// 	return
// }

// func (self *LinkedList) FeedTail2(feeder Feeder, observer Observer, abortOnError bool) (count int, err error) {
// 	count = 0
// 	// item := feeder.Next()
// 	for item, err1 := feeder.Next(); item != nil; item, err1 = feeder.Next() {
// 		if err1 != nil {
// 			if err == nil {
// 				err = err1
// 			}
// 			if abortOnError {
// 				break
// 			}
// 		}
// 		// fmt.Println("Item", count, item)
// 		node := self.PushBack(item)
// 		if observer != nil {
// 			observer.Observe(node, count)
// 		}
// 		count++
// 	}
// 	return
// }

func errorListEmpty() error {
	return fmt.Errorf("List is empty")
}

func errorOutOfRange() error {
	return fmt.Errorf("Out of range")
}
