/*
 * Copyright 2006 Phidgets Inc.  All rights reserved.
 */

package com.phidgets;

import com.phidgets.event.*;

import java.util.Iterator;
import java.util.LinkedList;

public class Phidget
{
	static {
		System.loadLibrary("phidget21");
	}

	private long handle = 0;

	public Phidget(long handle) {
		this.handle = handle;
	}

	private final long getHandle() {
		return handle;
	}

	public static native String getLibraryVersion();
	public native long getSerialNumber() throws PhidgetException;
	public native long getDeviceVersion() throws PhidgetException;
	public native String getDeviceType() throws PhidgetException;
	public native boolean isAttached() throws PhidgetException;
	public native String getTag() throws PhidgetException;
	public native void setTag(String tag) throws PhidgetException;

	public native void nativeClose() throws PhidgetException;
	public native void nativeDelete() throws PhidgetException;
	public native void nativeOpen(int serial) throws PhidgetException;
	public native void nativeRead() throws PhidgetException;

	public final void open(int serial) throws PhidgetException {
		enableEvents(true);
		nativeOpen(serial);
	}

	public final void openAny() throws PhidgetException {
		open(-1);
	}

	public final void close() throws PhidgetException {
		enableEvents(false);
		nativeClose();
	}

	private void enableEvents(boolean b) {
		enableAttachEvents(b && attachListeners.size() > 0);
		enableDetachEvents(b && detachListeners.size() > 0);
		enableErrorEvents(b && errorListeners.size() > 0);
		enableDeviceSpecificEvents(b);
	}

	public void waitForAttachment() throws PhidgetException {
		AttachListener al = new AttachListener() {
			public void attached(AttachEvent ae) {
				synchronized (ae.getSource()) {
					ae.getSource().notify();
				}
			}
		};
		addAttachListener(al);
		synchronized (this) {
			while (!isAttached()) {
				try {
					this.wait(0);
				} catch (InterruptedException e) {
				}
			}
		}
		synchronized (attachListeners) {
			removeAttachListener(al);
		}
	}

	/**
	 * Halts event dispatch while there are no listeners or the
	 * device is closed.  Should not affect Listeners.
	 */
	protected void enableDeviceSpecificEvents(boolean b) { }

	private LinkedList attachListeners = new LinkedList();
	private long nativeAttachHandler = 0;
	public final void addAttachListener(AttachListener l) {
		synchronized (attachListeners) {
			attachListeners.add(l);
			enableAttachEvents(true);
		}
	}
	public final void removeAttachListener(AttachListener l) {
		synchronized (attachListeners) {
			attachListeners.remove(l);
			enableAttachEvents(attachListeners.size() > 0);
		}
	}
	private void fireAttach(AttachEvent e) {
		synchronized (attachListeners) {
			for (Iterator it = attachListeners.iterator();
			  it.hasNext(); )
				((AttachListener)it.next()).attached(e);
		}
	}
	private native void enableAttachEvents(boolean b);

	private LinkedList errorListeners = new LinkedList();
	private long nativeErrorHandler = 0;
	public final void addErrorListener(ErrorListener l) {
		synchronized (errorListeners) {
			errorListeners.add(l);
			enableErrorEvents(true);
		}
	}
	public final void removeErrorListener(ErrorListener l) {
		synchronized (errorListeners) {
			errorListeners.remove(l);
			enableErrorEvents(errorListeners.size() > 0);
		}
	}
	private void fireError(ErrorEvent e) {
		synchronized (errorListeners) {
			for (Iterator it = errorListeners.iterator();
			  it.hasNext(); )
			((ErrorListener)it.next()).error(e);
		}
	}
	private native void enableErrorEvents(boolean b);

	private LinkedList detachListeners = new LinkedList();
	private long nativeDetachHandler = 0;
	public final void addDetachListener(DetachListener l) {
		synchronized (detachListeners) {
			detachListeners.add(l);
			enableDetachEvents(true);
		}
	}
	public final void removeDetachListener(DetachListener l) {
		synchronized (detachListeners) {
			detachListeners.remove(l);
			enableDetachEvents(detachListeners.size() > 0);
		}
	}
	private void fireDetach(DetachEvent e) {
		synchronized (detachListeners) {
			for (Iterator it = detachListeners.iterator();
			  it.hasNext(); )
			((DetachListener)it.next()).detached(e);
		}
	}
	private native void enableDetachEvents(boolean b);

	public String toString() {
		long dv = -1;
		long sn = -1;
		String dt = null;
		String at = "";

		try {
			dv = getDeviceVersion();
			sn = getSerialNumber();
			dt = getDeviceType();
			at = isAttached() ? " (attached)" : " (unattached)";
		} catch (PhidgetException e) {
			;
		} finally {
			if (dt == null)
				dt = getClass().getName().replaceFirst(
				  ".*\\.", "");
		}
		return dt + " v" + dv + " #" + sn + at;
	}

	public void finalize() {
		try {
			nativeDelete();
		} catch (Exception e) {
			;
		}
	}
}
