package example.stdhaskell.chapter09;

import haskell.prelude.AbstractShow;
import haskell.prelude.Bool;
import haskell.prelude.Function;
import haskell.prelude.Function2;
import haskell.prelude.List;
import haskell.prelude.text.Show;

/**
 * data Stack a = Stack [a]
 *     deriving Show
 */
public class Stack<A>
        extends AbstractShow<Stack<A>>
        implements Show<Stack<A>> {

    public final List<A> as;

    Stack(final List<A> as) {
        this.as = as;
    }

    /**
     * Stack [a]
     */
    public static <A> Stack<A> Stack_(final List<A> as) {
        return new Stack<A>(as);
    }

    /**
     * emptyStack :: Stack a
     * emptyStack = Stack []
     */
    public static <A> Stack<A> emptyStack() {
        return Stack_(List.<A>Nil());
    }

    /**
     * push :: a -> Stack a -> Stack a
     * push x (Stack xs) = Stack (x : xs)
     */
    public static <A> Function2<A, Stack<A>, Stack<A>> push() {
        return new Function2<A, Stack<A>, Stack<A>>("push") {
            @Override
            public Stack<A> apply(final A x, final Stack<A> stack) {
                return Stack_(List.Cons(x, stack.as));
            }
        };
    }

    /**
     * pop :: Stack a -> Stack a
     * pop (Stack [])      = error "pop emptyStack"
     * pop (Stack (_ : xs) = Stack xs
     */
    public static <A> Function<Stack<A>, Stack<A>> pop() {
        return new Function<Stack<A>, Stack<A>>("pop") {
            @Override
            public Stack<A> apply(final Stack<A> stack) {
                return Stack_(stack.as._tail_());
            }
        };
    }

    /**
     * top :: Stack a -> a
     * top (Stack[])      = error "top emptyStack"
     * top (Stack (x : _) = x
     */
    public static <A> Function<Stack<A>, A> top() {
        return new Function<Stack<A>, A>("top") {
            @Override
            public A apply(final Stack<A> stack) {
                return stack.as._head_();
            }
        };
    }

    /**
     * isEmpty :: Stack a -> Bool
     * isEmpty (Stack xs) = null xs
     */
    public static <A> Function<Stack<A>, Bool> isEmpty() {
        return new Function<Stack<A>, Bool>("isEmpty") {
            @Override
            public Bool apply(final Stack<A> stack) {
                return Bool.valueOf(stack.as._head_() == null);
            }
        };
    }

}
