package datastructures

import "errors"

// Stack a Last in First out data structure.
type Stack struct {
	elements []any
}

// Push add an element to the top of the stack
//
// Parameters:
//
//	any: an element to add to the top of the stack.
func (s *Stack) Push(el any) {
	s.elements = append(s.elements, el)
}

// Pop removes and returns the top element of the stack.
//
// Returns:
//
//	any: The top element of the stack
//
//	error: If the stack is found to be empty
func (s *Stack) Pop() (any, error) {
	if s.IsEmpty() {
		return nil, errors.New("empty stack")
	}
	el := s.elements[len(s.elements)-1]
	s.elements = s.elements[:len(s.elements)-1]
	return el, nil
}

// Peek checks the top item in the stack.
//
// Returns:
//
//	any: a copy of the top element
//
//	error: An error if the stack is empty
func (s *Stack) Peek() (any, error) {
	if s.IsEmpty() {
		return nil, errors.New("empty stack")
	}

	return s.elements[len(s.elements)-1], nil
}

// IsEmpty checks if the stack contains any elements.
//
// Returns:
//
//	bool: True is the stack is empty. False if the stack contains elements.
func (s *Stack) IsEmpty() bool {
	return s.Size() == 0
}

// Size checks this size of the stack
//
// Returns:
//
//	int: The number of elements on the stack
func (s *Stack) Size() int {
	return len(s.elements)
}