/*
	Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
	и других спецификаций для функционирования компактных приложений на языке
	Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

	Copyright © 2016, 2019 Малик Разработчик

	Это свободная программа: вы можете перераспространять ее и/или изменять
	ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
	в каком она была опубликована Фондом свободного программного обеспечения;
	либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

	Эта программа распространяется в надежде, что она будет полезной,
	но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
	или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
	общественной лицензии GNU.

	Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
	вместе с этой программой. Если это не так, см.
	<https://www.gnu.org/licenses/>.
*/


package javax.microedition.lcdui.game;

import javax.microedition.lcdui.*;

public class LayerManager extends Object
{
	private int viewLeft;
	private int viewTop;
	private int viewWidth;
	private int viewHeight;
	private int count;
	private Layer[] layers;
	private Object lock;

	public LayerManager()
	{
		this.viewWidth = Integer.MAX_VALUE;
		this.viewHeight = Integer.MAX_VALUE;
		this.layers = new Layer[2];
		this.lock = new Object();
	}

	public void paint(Graphics render, int left, int top)
	{
		int i;
		int clipLeft;
		int clipTop;
		int clipWidth;
		int clipHeight;
		int viewLeft;
		int viewTop;
		int viewWidth;
		int viewHeight;
		int viewRight;
		int viewBottom;
		int layerLeft;
		int layerTop;
		Layer[] layers;
		Layer layer;
		if(render == null)
		{
			throw new NullPointerException("LayerManager.paint: " +
					"параметр render равен нулевой ссылке.");
		}
		clipLeft = render.getClipX();
		clipTop = render.getClipY();
		clipWidth = render.getClipWidth();
		clipHeight = render.getClipHeight();
		synchronized(lock)
		{
			viewRight = (viewLeft = this.viewLeft) + (viewWidth = this.viewWidth);
			viewBottom = (viewTop = this.viewTop) + (viewHeight = this.viewHeight);
			render.translate(left - viewLeft, top - viewTop);
			render.clipRect(viewLeft, viewTop, viewWidth, viewHeight);
			try
			{
				for(layers = this.layers, i = this.count; i-- > 0; )
				{
					if((!(layer = layers[i]).isVisible()) ||
							(layerLeft = layer.getX()) >= viewRight ||
							(layerTop = layer.getY()) >= viewBottom ||
							layerLeft + layer.getWidth() <= viewLeft ||
							layerTop + layer.getHeight() <= viewTop)
					{
						continue;
					}
					layer.paint(render);
				}
			}
			finally
			{
				render.translate(viewLeft - left, viewTop - top);
				render.setClip(clipLeft, clipTop, clipWidth, clipHeight);
			}
		}
	}

	public void setViewWindow(int left, int top, int width, int height)
	{
		if(width < 0 || height < 0)
		{
			throw new IllegalArgumentException("LayerManager.setViewWindow: " +
					"размеры не могут быть отрицательными.");
		}
		synchronized(lock)
		{
			this.viewLeft = left;
			this.viewTop = top;
			this.viewWidth = width;
			this.viewHeight = height;
		}
	}

	public void insert(Layer layer, int layerIndex)
	{
		int c;
		int error;
		int count;
		int olderIndex;
		Layer[] layers;
		if(layer == null)
		{
			throw new NullPointerException("LayerManager.insert: " +
					"параметр layer равен нулевой ссылке.");
		}
		error = 0;
		synchronized(lock)
		{
			label0:
			{
				count = this.count;
				olderIndex = indexOf(layer);
				if(layerIndex < 0 || layerIndex > (olderIndex >= 0 ? count - 1 : count))
				{
					error = 1;
					break label0;
				}
				layers = this.layers;
				if(olderIndex >= 0)
				{
					if((c = count - olderIndex - 1) > 0)
					{
						Array.copy(layers, olderIndex + 1, layers, olderIndex, c);
					}
					count--;
				}
				else if(count == layers.length)
				{
					Array.copy(layers, 0, layers = this.layers = new Layer[count << 1], 0, count);
				}
				if((c = count - layerIndex) > 0)
				{
					Array.copy(layers, layerIndex, layers, layerIndex + 1, c);
				}
				layers[layerIndex] = layer;
				this.count = ++count;
			}
		}
		if(error == 1)
		{
			throw new IndexOutOfBoundsException("LayerManager.insert: " +
					"параметр layerIndex выходит из диапазона.");
		}
	}

	public void remove(Layer layer)
	{
		int c;
		int count;
		int layerIndex;
		Layer[] layers;
		if(layer == null)
		{
			throw new NullPointerException("LayerManager.remove: " +
					"параметр layer равен нулевой ссылке.");
		}
		synchronized(lock)
		{
			if((layerIndex = indexOf(layer)) >= 0)
			{
				count = this.count;
				layers = this.layers;
				if((c = count - layerIndex - 1) > 0)
				{
					Array.copy(layers, layerIndex + 1, layers, layerIndex, c);
				}
				layers[--count] = null;
				this.count = count;
			}
		}
	}

	public void append(Layer layer)
	{
		int c;
		int count;
		int olderIndex;
		Layer[] layers;
		if(layer == null)
		{
			throw new NullPointerException("LayerManager.append: " +
					"параметр layer равен нулевой ссылке.");
		}
		synchronized(lock)
		{
			count = this.count;
			layers = this.layers;
			if((olderIndex = indexOf(layer)) >= 0)
			{
				if((c = count - olderIndex - 1) > 0)
				{
					Array.copy(layers, olderIndex + 1, layers, olderIndex, c);
				}
				count--;
			}
			else if(count == layers.length)
			{
				Array.copy(layers, 0, layers = this.layers = new Layer[count << 1], 0, count);
			}
			layers[count++] = layer;
			this.count = count;
		}
	}

	public int getSize()
	{
		return count;
	}

	public Layer getLayerAt(int layerIndex)
	{
		int error;
		Layer result;
		error = 0;
		synchronized(lock)
		{
			label0:
			{
				if(layerIndex < 0 || layerIndex >= count)
				{
					error = 1;
					result = null;
					break label0;
				}
				result = layers[layerIndex];
			}
		}
		if(error == 1)
		{
			throw new IndexOutOfBoundsException("LayerManager.getLayerAt: " +
					"параметр layerIndex выходит из диапазона.");
		}
		return result;
	}

	private int indexOf(Layer layer)
	{
		int i;
		Layer[] layers;
		for(layers = this.layers, i = this.count; i-- > 0; )
		{
			if(layers[i] == layer)
			{
				return i;
			}
		}
		return -1;
	}
}
