package fr.upem.concurrence.td3;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ExchangerNonReusable<V> {

        private enum State  {EMPTY, FIRST_HERE,FULL};
        private State state;
        private V firstValue;
        private V secondValue;
        private final ReentrantLock lock = new ReentrantLock();
        private final Condition isFull = lock.newCondition();

        public ExchangerNonReusable(){
            lock.lock();
            try{
                state=State.EMPTY;
            } finally {
                lock.unlock();
            }
        }

        public V exchange(V value) throws InterruptedException {
            lock.lock();
            try {
                switch (state) {
                    case EMPTY:
                        firstValue = value;
                        state = State.FIRST_HERE;
                        while (state != State.FULL) {
                            isFull.await();
                        }
                        return secondValue;
                    case FIRST_HERE:
                        secondValue = value;
                        state = State.FULL;
                        isFull.signal();
                        return firstValue;
                    default:
                        throw new AssertionError();
                }
            } finally {
                lock.unlock();
            }
        }

    public static void main(String[] args) {
        ExchangerNonReusable<Integer> exchanger = new ExchangerNonReusable<>();
        Thread t1 = new Thread(() -> {
            try {
                System.out.println("Thread 1 exchanged with Thread " + exchanger.exchange(1));
            } catch (InterruptedException e) {
                throw new AssertionError(e);
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                System.out.println("Thread 2 exchanged with Thread " + exchanger.exchange(2));
            } catch (InterruptedException e) {
                throw new AssertionError(e);
            }
        });
        Thread t3 = new Thread(() -> {
            try {
                System.out.println("Thread 3 exchanged with Thread " + exchanger.exchange(3));
            } catch (InterruptedException e) {
                throw new AssertionError(e);
            }
        });
        Thread t4 = new Thread(() -> {
            try {
                System.out.println("Thread 4 exchanged with Thread " + exchanger.exchange(4));
            } catch (InterruptedException e) {
                throw new AssertionError(e);
            }
        });
        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }
}
