package ibis.ipl.registry.central.client;

import ibis.ipl.registry.central.Member;
import ibis.util.ThreadPool;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Sends events to clients from the server.
 */
final class IterativeEventPusher extends Thread {

    private static final int THREADS = 10;

    private class WorkQ {
        private List<Member> q;
        private int count;

        WorkQ(Member[] work) {
            // Arrays.asList list does not support remove, so do this "trick"
            q = new LinkedList<Member>();
            q.addAll(Arrays.asList(work));

            count = this.q.size();
        }

        synchronized Member next() {
            if (q.isEmpty()) {
                return null;
            }

            return q.remove(0);
        }

        synchronized void doneJob() {
            count--;

            if (count <= 0) {
                notifyAll();
            }
        }

        synchronized void waitUntilDone() {
            while (count > 0) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    // IGNORE
                }
            }
        }
    }

    private class EventPusherThread implements Runnable {

        WorkQ workQ;

        EventPusherThread(WorkQ workQ) {
            this.workQ = workQ;

            ThreadPool.createNew(this, "event pusher thread");
        }

        public void run() {
            while (true) {
                Member work = workQ.next();

                if (work == null) {
                    // done pushing
                    return;
                }

                logger.debug("pushing to " + work);

                commHandler.forward(work.getIbis());
                workQ.doneJob();
            }
        }
    }

    private static final Logger logger = LoggerFactory.getLogger(IterativeEventPusher.class);

    private final Pool pool;
    private final CommunicationHandler commHandler;

    IterativeEventPusher(Pool pool, CommunicationHandler commHandler) {
        this.pool = pool;
        this.commHandler = commHandler;
    }

    public void run() {
        while (!pool.isStopped()) {
            int eventTime = pool.getTime();

            Member[] children = pool.getChildren();

            logger.debug("updating " + children.length +
                    " children in pool to event-time " + eventTime);

            WorkQ workQ = new WorkQ(children);

            int threads = Math.min(THREADS, children.length);
            for (int i = 0; i < threads; i++) {
                new EventPusherThread(workQ);
            }

            workQ.waitUntilDone();

            logger.debug("DONE updating nodes in pool to event-time "
                    + eventTime);

            pool.waitForEventTime(eventTime + 1, 1000);
        }
    }
}
