import { ILoopFunction } from '@mezzy/function-types';
import List from './list';


export class Stack<T> {
    /**
     * Creates an empty Stack.
     * @class A Stack is a Last-In-First-Out (LIFO) data structure, the last
     * element added to the stack will be the first one to be deleted. This
     * implementation uses a linked list as a container.
     * @constructor
     */
    constructor() { this._list = new List<T>(); }


    /**
     * List containing the elements.
     */
    private _list:List<T>;


    get isEmpty():boolean { return this._list.isEmpty; }


    /**
     * Pushes an item onto the top of this stack.
     * @param {Object} elem the element to be pushed onto this stack.
     * @return {boolean} true if the element was pushed or false if it is undefined.
     */
    push(elem:T) { return this._list.add(elem, 0); }


    /**
     * Pushes an item onto the top of this stack.
     * @param {Object} elem the element to be pushed onto this stack.
     * @return {boolean} true if the element was pushed or false if it is undefined.
     */
    add(elem:T) { return this._list.add(elem, 0); }


    /**
     * Removes the object at the top of this stack and returns that object.
     * @return {*} the object at the top of this stack or undefined if the stack is empty.
     */
    pop():T { return this._list.deleteAtIndex(0); }


    /**
     * Looks at the object at the top of this stack without removing it from the stack.
     * @return {*} the object at the top of this stack or undefined if the stack is empty.
     */
    peek():T { return this._list.first; }


    /**
     * Returns the number of elements in this stack.
     * @return {number} the number of elements in this stack.
     */
    get size():number { return this._list.size; }


    /**
     * Returns true if this stack has the specified element.
     * <p>If the elements inside this stack are
     * not comparable with the === operator, a custom equals function should be
     * provided to perform searches, the function must receive two arguments and
     * return true if they are equal, false otherwise. Example:</p>
     *
     * <pre>
     * let petsAreEqualByName (pet1, pet2) {
     *  return pet1.key === pet2.key;
     * }
     * </pre>
     * @param {Object} elem element to search for.
     * @param {function(Object,Object):boolean=} equalsFunction optional
     * function to check if two elements are equal.
     * @return {boolean} true if this stack has the specified element,
     * false otherwise.
     */
    has(elem:T) { return this._list.has(elem); }
    clear():void {  this._list.clear(); }
    forEach(callback:ILoopFunction<T>) { this._list.forEach(callback); }
} // End class


export default Stack;
