package org.multiverse.stms.gamma.integration.classic;

import org.junit.Before;
import org.junit.Test;
import org.multiverse.TestThread;
import org.multiverse.api.Txn;
import org.multiverse.api.callables.TxnVoidCallable;
import org.multiverse.api.references.TxnBoolean;
import org.multiverse.api.references.TxnInteger;

import static org.multiverse.TestUtils.*;
import static org.multiverse.api.StmUtils.*;
import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn;

/**
 * http://en.wikipedia.org/wiki/Sleeping_barber_problem
 */
public class SleepingBarberStressTest {

    private BarberShop barberShop;
    private volatile boolean stop;

    @Before
    public void setUp() {
        clearThreadLocalTxn();
        barberShop = new BarberShop();
        stop = false;
    }

    @Test
    public void test() {
        BarberThread thread = new BarberThread();
        CustomerSpawnThread spawnThread = new CustomerSpawnThread();

        startAll(thread, spawnThread);
        sleepMs(30 * 1000);
        stop = true;
        joinAll(thread, spawnThread);
    }

    @SuppressWarnings({"ObjectAllocationInLoop"})
    class CustomerSpawnThread extends TestThread {
        public CustomerSpawnThread() {
            super("CustomerSpawnThread");
        }

        @Override
        public void doRun() throws Exception {
            int customerId = 1;
            while (!stop) {
                CustomerThread customerThread = new CustomerThread(customerId);
                customerThread.start();
                customerId++;
                sleepRandomMs(100);
            }
        }
    }

    class BarberThread extends TestThread {
        public BarberThread() {
            super("BarberThread");
        }

        @Override
        public void doRun() {
            TxnVoidCallable callable = new TxnVoidCallable() {
                @Override
                public void call(Txn tx) throws Exception {
                    //todo
                }
            };

            while (!stop) {
                atomic(callable);
            }

            barberShop.atomicClose();
        }
    }

    class CustomerThread extends TestThread {
        public CustomerThread(int id) {
            super("CustomerThread-" + id);
        }

        @Override
        public void doRun() throws Exception {
            atomic(new TxnVoidCallable() {
                @Override
                public void call(Txn tx) throws Exception {
                    if (barberShop.closed.get()) {
                        return;
                    }

                    if (barberShop.freeSeats.get() == 0) {
                        return;
                    }

                    //todo
                }
            });
        }
    }

    static class BarberShop {
        private TxnBoolean closed = newTxnBoolean(false);
        private TxnInteger freeSeats = newTxnInteger(5);

        void atomicClose() {
            closed.atomicSet(false);
        }
    }
}
