/
codebase/src/net/sourceforge/pain/admin/console/command/
codebase/src/net/sourceforge/pain/data/role/
codebase/src/net/sourceforge/pain/network/console/telnet/
codebase/src/net/sourceforge/pain/network/guitool/
codebase/src/net/sourceforge/pain/plugin/
codebase/src/net/sourceforge/pain/util/
db/src/net/sourceforge/pain/util/
gui/
gui/lib/
gui/src/net/sourceforge/pain/tools/guitool/dbbrowse/
gui/src/net/sourceforge/pain/tools/guitool/dialog/
gui/src/net/sourceforge/pain/tools/guitool/menu/
gui/src/net/sourceforge/pain/tools/guitool/resources/
gui/src/net/sourceforge/pain/tools/guitool/resources/images/
gui/src/net/sourceforge/pain/tools/guitool/resources/images/explorer/
mudlibs/tinylib/
mudlibs/tinylib/area/
mudlibs/tinylib/etc/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/data/affect/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/data/prototype/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/data/trigger/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/affect/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/event/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/event/deploy/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/event/guitool/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/event/guitool/event/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/fn/util/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/trigger/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/trigger/impl/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/plugin/command/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/plugin/reset/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/plugin/shutdown/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/plugin/social/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/util/
tests/
tests/src/
tests/src/net/sourceforge/pain/db/data/
package net.sourceforge.pain;

import net.sourceforge.pain.data.*;
import net.sourceforge.pain.util.*;


/**
 * Codebase internal clocks (pulse) impl.
 */
public final class Pulse implements Runnable {

    public static final int PULSE_PER_SCD = 10;
    public static final int PULSE_PER_MIN = PULSE_PER_SCD * 60;


    private final Object monitor = new Object();

    private final int msec_in_pulse = 1000 / PULSE_PER_SCD;

    private final Listeners active = new Listeners();
    private final Listeners toRemove = new Listeners();

    private boolean started = false;
    private CodebasePulse codebasePulse = null;
    private int pulse = 0;


    Pulse() {
    }

    public void addListener(PulseListener l) {
        Log.debug("adding listener:" + l);
        active.add(l);
    }

    public void removeListener(PulseListener l) {
        Log.debug("adding to remove listener:" + l);
        toRemove.add(l);
    }

    public void run() {
        codebasePulse = Codebase.getCodebaseData().getCodebasePulse();
        pulse = codebasePulse.getPulse();
        Log.debug("CodebasePulser start:" + pulse);
        started = true;
        long nextPulseTime = System.currentTimeMillis() + msec_in_pulse;
        long timeToWait;
        while (started) {
            try {
                timeToWait = nextPulseTime - System.currentTimeMillis();
                if (timeToWait > 10) {
                    synchronized (monitor) {
                        monitor.wait(timeToWait);
                    }
                }
                nextPulseTime = System.currentTimeMillis() + msec_in_pulse;
                if (toRemove.size > 0) {
                    processRemoved();
                }

                pulse++;
                codebasePulse.setPulse(pulse);
                final PulseListener[] data = active.data;
                final int size = active.size;
                for (int i = 0; i < size; i++) {
                    try {
                        data[i].pulse(pulse);
                    } catch (Exception e) {
                        Log.error(e);
                    }
                }
            } catch (Exception e) {
                Log.error(e);
            } catch (Error e) {
                Log.error("CRITICAL BUG! ERROR THROWN!", e);
//				new Thread(this).start();
//				break;
            }
        }
        Codebase.shutdown();
    }

    private synchronized void processRemoved() {
        for (int i = 0; i < toRemove.size; i++) {
            active.remove(toRemove.data[i]);
        }
        toRemove.clear();
    }

    /**
     * @return current time in pulse
     */
    public int currentTime() {
        return pulse;
    }

    /**
     * the way to stop codebase.
     */
    public void stopPulse() {
        started = false;
    }

    static final class Listeners {
        PulseListener[] data = new PulseListener[10];
        int size = 0;

        synchronized void add(PulseListener l) {
            if (size == data.length) {
                PulseListener[] newData = new PulseListener[size * 2];
                System.arraycopy(data, 0, newData, 0, size);
                data = newData;
            }
            data[size] = l;
            size++;
        }

        synchronized void remove(PulseListener l) {
            for (int i = 0; i < size; i++) {
                if (data[i] == l) {
                    int numMoved = size - i - 1;
                    if (numMoved > 0) {
                        System.arraycopy(data, i + 1, data, i, numMoved);
                    }
                    data[size] = null;
                    size--;
                }
            }
        }

        synchronized void clear() {
            for (int i = 0; i < size; i++) {
                data[i] = null;
            }
            size = 0;
        }
    }
}