// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.resource.are.viewer;
import java.util.ArrayList;
import java.util.List;
import org.infinity.gui.layeritem.AbstractLayerItem;
import org.infinity.resource.AbstractStruct;
import org.infinity.resource.StructEntry;
import org.infinity.resource.are.AreResource;
import org.infinity.resource.are.viewer.ViewerConstants.LayerType;
import org.infinity.resource.wed.WedResource;
/**
* Common base class for layer-specific managers.
*/
public abstract class BasicLayer<E extends LayerObject>
{
private final LayerType layerType;
private final int layerTypeIndex;
private final List<E> listObjects = new ArrayList<E>();
private final AreaViewer viewer;
private AbstractStruct parent;
private int schedule;
private boolean visible, initialized, scheduleEnabled;
/**
* Initializes the current layer.
* @param parent The parent resource of the layer (either AreResource or WedResource).
* @param type The type/identifier of the layer.
*/
public BasicLayer(AbstractStruct parent, LayerType type, AreaViewer viewer)
{
// setting parent resource
if (parent instanceof AreResource || parent instanceof WedResource) {
this.parent = parent;
} else {
this.parent = null;
}
// setting layer type
this.layerType = type;
// getting associated layer type index
int idx = -1;
LayerType[] lt = LayerType.values();
for (int i = 0; i < lt.length; i++) {
if (lt[i] == this.layerType) {
idx = i;
}
}
layerTypeIndex = idx;
// setting associated area viewer instance
this.viewer = viewer;
}
/**
* Returns the registered AreaViewer instance.
*/
public AreaViewer getViewer()
{
return viewer;
}
/**
* Returns whether the parent structure is of type AreResource.
*/
public boolean hasAre()
{
return (parent instanceof AreResource);
}
/**
* Returns the parent as AreResource structure if available.
*/
public AreResource getAre()
{
return (parent instanceof AreResource) ? (AreResource)parent : null;
}
/**
* Returns whether the parent structure is of type WedResource.
*/
public boolean hasWed()
{
return (parent instanceof WedResource);
}
/**
* Returns the parent as WedResource structure if available.
*/
public WedResource getWed()
{
return (parent instanceof WedResource) ? (WedResource)parent : null;
}
/**
* Returns the layer type of this specific layer.
* @return The layer type
*/
public LayerType getLayerType()
{
return layerType;
}
/**
* Returns the index of the layer type.
* @return Index of layer type.
*/
public int getLayerTypeIndex()
{
return layerTypeIndex;
}
/**
* Returns the number of objects in this layer.
* @return Number of layer objects.
*/
public int getLayerObjectCount()
{
return listObjects.size();
}
/**
* Returns the layer object at the specified index.
* @param index The index of the layer object.
* @return The layer object, of {@code null} if not available.
*/
public E getLayerObject(int index)
{
if (index >= 0 && index < listObjects.size()) {
return listObjects.get(index);
} else {
return null;
}
}
/**
* Returns the list of layer objects for direct manipulation.
* @return List of layer objects.
*/
public List<E> getLayerObjects()
{
return listObjects;
}
/**
* Returns whether the whole layer of items is marked as visible. (Note: Does not work correctly
* if the visibility state of individual items are overridden.)
*/
public boolean isLayerVisible()
{
return visible;
}
/**
* Sets the visibility state of all items in the layer.
*/
public void setLayerVisible(boolean visible)
{
for (int i = 0, size = listObjects.size(); i < size; i++) {
boolean state = visible && (!isScheduleEnabled() || (isScheduleEnabled() && isScheduled(i)));
E obj = listObjects.get(i);
AbstractLayerItem[] items = obj.getLayerItems();
for (int j = 0; j < items.length; j++) {
items[j].setVisible(state);
}
}
this.visible = visible;
}
/**
* Returns the layer object containing the specified layer item.
* @return The object, if it has been found, {@code null} otherwise.
*/
public E getLayerObjectOf(AbstractLayerItem item)
{
if (item != null) {
for (int i = 0, size = listObjects.size(); i < size; i++) {
E obj = listObjects.get(i);
AbstractLayerItem[] items = obj.getLayerItems();
for (int j = 0; j < items.length; j++) {
if (items[j] == item) {
return obj;
}
}
}
}
return null;
}
/**
* Loads all available objects of this layer if it hasn't been loaded yet.
* @param forced If {@code true}, always (re-)loads the current layer, even if it has been loaded already.
* @return The number of initialized layer objects.
*/
public abstract int loadLayer(boolean forced);
/**
* Removes all objects of the layer from memory. Additionally all associated layer items will be
* removed from their associated container(s).
*/
public void close()
{
if (getViewer() != null) {
for (int i = 0, size = listObjects.size(); i < size; i++) {
listObjects.get(i).close();
}
}
listObjects.clear();
setInitialized(false);
}
/**
* Returns whether schedules will be considered when querying {@link #isScheduled(int)}.
* @return {@code true} if schedules will be considered, {@code false} otherwise.
*/
public boolean isScheduleEnabled()
{
return scheduleEnabled;
}
/**
* Set whether schedules will be considered when querying {@link #isScheduled(int)}.
* When setting {@code false}, {@link #isScheduled(int)} will always return true.
*/
public void setScheduleEnabled(boolean enable)
{
if (enable != scheduleEnabled) {
scheduleEnabled = enable;
setLayerVisible(isLayerVisible());
}
}
/**
* Set the current schedule. It can be used on layer objects that support schedules to check against.
* @param schedule The schedule value (schedule = hour - 1)
*/
public void setSchedule(int schedule)
{
if (schedule < 0) schedule = 0; else if (schedule > 23) schedule = 23;
if (this.schedule != schedule) {
this.schedule = schedule;
setLayerVisible(isLayerVisible());
}
}
/**
* Returns the currently set schedule.
* @return The current schedule value.
*/
public int getSchedule()
{
return schedule;
}
/**
* Returns whether the layer object at the specified index is active at the currently set schedule.
* @param index The index of the layer object to check.
* @return {@code true} if the layer object is scheduled, {@code false} otherwise.
* (Note: Layer objects without schedule always return {@code true}.)
*/
public boolean isScheduled(int index)
{
E obj = getLayerObject(index);
if (obj != null) {
return !isScheduleEnabled() || obj.isScheduled(getSchedule());
} else {
return false;
}
}
/**
* Returns the number of objects in this layer as a formatted string.
* @return A formatted string telling about the number of objects in this layer.
*/
public abstract String getAvailability();
// Creates a list of structures of the specified type from the parent structure
protected List<StructEntry> getStructures(int baseOfs, int count, Class<? extends StructEntry> type)
{
List<StructEntry> listStruct = new ArrayList<StructEntry>();
if (getParent() != null && baseOfs >= 0 && count >= 0 && type != null) {
List<StructEntry> list = getParent().getList();
int cnt = 0;
for (int i = 0, size = list.size(); i < size; i++) {
if (list.get(i).getOffset() >= baseOfs && type.isAssignableFrom(list.get(i).getClass())) {
listStruct.add(list.get(i));
cnt++;
if (cnt >= count) {
break;
}
}
}
}
return listStruct;
}
// Convenience method: sets required listeners
protected void setListeners(LayerObject obj)
{
if (obj != null) {
AbstractLayerItem[] items = obj.getLayerItems();
for (int i = 0; i < items.length; i++) {
if (items[i] != null) {
items[i].addActionListener(viewer.getListeners());
items[i].addLayerItemListener(viewer.getListeners());
items[i].addMouseListener(viewer.getListeners());
items[i].addMouseMotionListener(viewer.getListeners());
}
}
}
}
/**
* [For internal use only] Returns whether this layer has been loaded at least once.
*/
protected boolean isInitialized()
{
return initialized;
}
/**
* [For internal use only] Marks the current layer as loaded.
*/
protected void setInitialized(boolean set)
{
initialized = set;
}
/**
* [For internal use only] Sets the global visibility state of layer items without actually
* touching the layer items. Use this method if you need to override {@link #setLayerVisible(boolean)}.
*/
protected void setVisibilityState(boolean state)
{
visible = state;
}
// Returns the parent structure of the layer objects regardless of type.
private AbstractStruct getParent()
{
return parent;
}
}