/*
 * Java-Gnome Bindings Library
 *
 * Copyright 1998-2004 the Java-Gnome Team, all rights reserved.
 *
 * The Java-Gnome bindings library is free software distributed under
 * the terms of the GNU Library General Public License version 2.
 */

package org.gnu.gtk;

import java.util.Vector;

import org.gnu.glib.EventMap;
import org.gnu.glib.EventType;
import org.gnu.glib.GObject;
import org.gnu.glib.Type;
import org.gnu.gtk.event.ContainerEvent;
import org.gnu.gtk.event.ContainerListener;
import org.gnu.glib.Handle;

/**
 * The Container widget is a base class for container widgets. Widgets that
 * inherit from Container have the ability to contain, position, size, and
 * display one or more other widgets.
 */
public class Container extends Widget {

    protected Container(Handle handle) {
        super(handle);
    }

    /**
     * Internal static factory method to be used by Java-Gnome only.
     */
    protected static Container getContainer(Handle handle) {
        if (handle == null)
            return null;

        Container obj = (Container) GObject.getGObjectFromHandle(handle);

        if (obj == null)
            obj = new Container(handle);

        return obj;
    }

    /**
     * Adds widget to the Container.
     * 
     * @param widget
     *            The Widget to be added to this Container.
     */
    public void add(Widget widget) {
        checkState();
        Container.gtk_container_add(getHandle(), widget.getHandle());
    }

    /**
     * Remove a Widget from the Container.
     * 
     * @param widget
     *            The Widget to remove from the Container.
     */
    public void remove(Widget widget) {
        checkState();
        Container.gtk_container_remove(getHandle(), widget.getHandle());
    }

    /**
     * Returns the ResizeMode for the Container.
     * 
     * @return The ResizeMode for the Container.
     */
    public ResizeMode getResizeMode() {
        checkState();
        int val = Container.gtk_container_get_resize_mode(getHandle());
        return ResizeMode.intern(val);
    }

    /**
     * Sets the ResizeMode for the Container.
     * 
     * @param mode
     *            The ResizeMode.
     */
    public void setResizeMode(ResizeMode mode) {
        checkState();
        Container.gtk_container_set_resize_mode(getHandle(), mode.getValue());
    }

    /**
     * Retrieves the border width for the Container.
     * 
     * @return The border width.
     */
    public int getBorderWidth() {
        checkState();
        return Container.gtk_container_get_border_width(getHandle());
    }

    /**
     * Sets the border width for the Container.
     * 
     * @param width
     *            The border width.
     */
    public void setBorderWidth(int width) {
        checkState();
        Container.gtk_container_set_border_width(getHandle(), width);
    }

    /**
     * Informs the container to resize all of its' children based on the size
     * requirements of the children.
     */
    public void resizeChildren() {
        checkState();
        Container.gtk_container_resize_children(getHandle());
    }

    /**
     * Get an array of all children of this container.
     * 
     * @return An array contain all children of this container or null.
     */
    public Widget[] getChildren() {
        checkState();
        Handle[] handles = gtk_container_get_children(getHandle());
        if (handles == null) {
            return null;
        }

        Widget[] widgets = new Widget[handles.length];

        for (int i = 0; i < handles.length; i++) {
            // Checks if the Handle has an object associated with it, and if
            // not,
            // try to create the correct subclass of Widget by using reflection.
            // If this fails, then simply create a Widget and return that.
            widgets[i] = (Widget) getGObjectFromHandle(handles[i]);

            if (widgets[i] != null) {
                continue;
            }

            try {
                widgets[i] = Widget.makeWidget(handles[i]);
            } catch (ClassNotFoundException cnfe) {
                widgets[i] = new Widget(handles[i]);
            }
        }
        return widgets;
    }

    /**
     * Retrieve the runtime type used by the GLib library.
     */
    public static Type getType() {
        return new Type(gtk_container_get_type());
    }

    /***************************************************************************
     * Event handling support
     **************************************************************************/

    /**
     * Listeners for handling button events
     */
    private Vector containerListeners = null;

    /**
     * Register an object to handle container events.
     * 
     * @see org.gnu.gtk.event.ContainerListener
     */
    public void addListener(ContainerListener listener) {
        checkState();
        // Don't add the listener a second time if it is in the Vector.
        int i = findListener(containerListeners, listener);
        if (i == -1) {
            if (null == containerListeners) {
                evtMap.initialize(this, ContainerEvent.Type.ADD);
                evtMap.initialize(this, ContainerEvent.Type.CHECK_RESIZE);
                evtMap.initialize(this, ContainerEvent.Type.REMOVE);
                evtMap.initialize(this, ContainerEvent.Type.SET_FOCUS_CHILD);
                containerListeners = new Vector();
            }
            containerListeners.addElement(listener);
        }
    }

    /**
     * Removes a listener
     * 
     * @see #addListener(ContainerListener)
     */
    public void removeListener(ContainerListener listener) {
        checkState();
        int i = findListener(containerListeners, listener);
        if (i > -1) {
            containerListeners.remove(i);
        }
        if (0 == containerListeners.size()) {
            evtMap.uninitialize(this, ContainerEvent.Type.ADD);
            evtMap.uninitialize(this, ContainerEvent.Type.CHECK_RESIZE);
            evtMap.uninitialize(this, ContainerEvent.Type.REMOVE);
            evtMap.uninitialize(this, ContainerEvent.Type.SET_FOCUS_CHILD);
            containerListeners = null;
        }
    }

    protected void fireContainerEvent(ContainerEvent event) {
        if (null == containerListeners) {
            return;
        }
        int size = containerListeners.size();
        int i = 0;
        while (i < size) {
            ContainerListener bl = (ContainerListener) containerListeners
                    .elementAt(i);
            bl.containerEvent(event);
            i++;
        }
    }

    private void handleAdd(Handle childHandle) {
        fireContainerEvent(new ContainerEvent(this, ContainerEvent.Type.ADD));
    }

    private void handleCheckResize() {
        fireContainerEvent(new ContainerEvent(this,
                ContainerEvent.Type.CHECK_RESIZE));
    }

    private void handleRemove(Handle childHandle) {
        fireContainerEvent(new ContainerEvent(this, ContainerEvent.Type.REMOVE));
    }

    private void handleSetFocusChild(Handle childHandle) {
        fireContainerEvent(new ContainerEvent(this,
                ContainerEvent.Type.SET_FOCUS_CHILD));
    }

    public Class getEventListenerClass(String signal) {
        checkState();
        Class cls = evtMap.getEventListenerClass(signal);
        if (cls == null)
            cls = super.getEventListenerClass(signal);
        return cls;
    }

    public EventType getEventType(String signal) {
        checkState();
        EventType et = evtMap.getEventType(signal);
        if (et == null)
            et = super.getEventType(signal);
        return et;
    }

    private static EventMap evtMap = new EventMap();
    static {
        addEvents(evtMap);
    }

    /**
     * Implementation method to build an EventMap for this widget class. Not
     * useful (or supported) for application use.
     */
    private static void addEvents(EventMap anEvtMap) {
        anEvtMap.addEvent("add", "handleAdd", ContainerEvent.Type.ADD,
                ContainerListener.class);
        anEvtMap.addEvent("check_resize", "handleCheckResize",
                ContainerEvent.Type.CHECK_RESIZE, ContainerListener.class);
        anEvtMap.addEvent("remove", "handleRemove", ContainerEvent.Type.REMOVE,
                ContainerListener.class);
        anEvtMap.addEvent("set_focus_child", "handleSetFocusChild",
                ContainerEvent.Type.SET_FOCUS_CHILD, ContainerListener.class);
    }

    native static final protected int gtk_container_get_type();

    native static final protected void gtk_container_set_border_width(
            Handle container, int borderWidth);

    native static final protected int gtk_container_get_border_width(
            Handle container);

    native static final protected void gtk_container_add(Handle container,
            Handle widget);

    native static final protected void gtk_container_remove(Handle container,
            Handle widget);

    native static final protected void gtk_container_set_resize_mode(
            Handle container, int mode);

    native static final protected int gtk_container_get_resize_mode(
            Handle container);

    native static final protected void gtk_container_check_resize(
            Handle container);

    native static final protected Handle[] gtk_container_get_children(
            Handle container);

    native static final protected void gtk_container_propagate_expose(
            Handle container, Handle child, Handle event);

    native static final protected void gtk_container_set_focus_chain(
            Handle container, Handle focusableWidgets);

    native static final protected boolean gtk_container_get_focus_chain(
            Handle container, Handle focusableWidgets);

    native static final protected void gtk_container_unset_focus_chain(
            Handle container);

    native static final protected void gtk_container_set_reallocate_redraws(
            Handle container, boolean needsRedraws);

    native static final protected void gtk_container_set_focus_child(
            Handle container, Handle child);

    native static final protected void gtk_container_set_focus_vadjustment(
            Handle container, Handle adjustment);

    native static final protected Handle gtk_container_get_focus_vadjustment(
            Handle container);

    native static final protected void gtk_container_set_focus_hadjustment(
            Handle container, Handle adjustment);

    native static final protected Handle gtk_container_get_focus_hadjustment(
            Handle container);

    native static final protected void gtk_container_resize_children(
            Handle container);

    native static final protected int gtk_container_child_type(Handle container);

    /*
     * Deprecated functions. native static final private void
     * gtk_container_foreach_full(Handle container, int callback, int marshal,
     * gpointer callback_data, int notify);
     */
}
