package com.tsi.app.common.utils;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;

public class LockFreeStack<T> {

    private AtomicReference<StackNode<T> > headNode = new AtomicReference<StackNode<T> >();
    private AtomicInteger remains = new AtomicInteger(0);

    // Push operation
    public boolean offer(T value)
    {
        StackNode<T> newHead = new StackNode<T>(value);

        // CAS loop defined
        while (true) {
            StackNode<T> currentHeadNode = headNode.get();
            newHead.next = currentHeadNode;

            // perform CAS operation before setting new
            // value
            if (headNode.compareAndSet(currentHeadNode, newHead)) {
                break;
            }
            else {
                // waiting for a nanosecond
                LockSupport.parkNanos(1);
            }
        }

        // getting the value atomically
        remains.incrementAndGet();
        return true;
    }

    // Pop function
    public T take()
    {
        StackNode<T> currentHeadNode = headNode.get();

        // CAS loop defined
        while (currentHeadNode != null) {
            StackNode<T> newHead = currentHeadNode.next;
            if (headNode.compareAndSet(currentHeadNode,
                    newHead)) {
                break;
            }
            else {
                // waiting for a nanosecond
                LockSupport.parkNanos(1);
                currentHeadNode = headNode.get();
            }
        }

        T t = currentHeadNode != null ? currentHeadNode.value : null;

        if (t != null)
            remains.decrementAndGet();

        return t;
    }

    public int size() {
        return (int)remains.get();
    }

    private static class StackNode<T> {
        T value;
        StackNode<T> next;
        StackNode(T value) { this.value = value; }

        public T getValue() { return this.value; }
    }
}
